From a8f6a63d4929737428fa88fc0bccb1791c1bf729 Mon Sep 17 00:00:00 2001 From: vivet Date: Thu, 4 Jun 2026 18:24:52 +0200 Subject: [PATCH 1/2] Removed all --- .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 44 - Api.ApiClients.Entity/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.ApiClients.Entity/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.ApiClients.Entity.csproj | 23 - .../Api.ApiClients.Entity.Models.csproj | 75 -- ...pi.ApiClients.Entity.Service.Models.csproj | 77 -- .../Api/NanoApiClient.cs | 8 - .../Criterias/ExampleQueryCriteria.cs | 34 - .../Example.cs | 14 - .../Api.ApiClients.Entity.Service.csproj | 37 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260422105215_Initial.Designer.cs | 651 -------------- .../Migrations/20260422105215_Initial.cs | 601 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 648 ------------- .../Api.ApiClients.Entity.Service/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 16 - .../Api.ApiClients.Entity.sln | 159 ---- .../Api.ApiClients.Entity.csproj | 38 - .../Controllers/ExamplesController.cs | 732 --------------- .../Api.ApiClients.Entity/Dockerfile.Local | 6 - .../Api.ApiClients.Entity/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.ApiClients.Entity/appsettings.json | 25 - Api.ApiClients.Entity/Dockerfile | 35 - Api.ApiClients.Entity/LICENSE | 18 - Api.ApiClients.Entity/README.md | 30 - Api.ApiClients.Entity/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 29 - Api.ApiClients.RootLogIn/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.ApiClients.RootLogIn/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.ApiClients.RootLogIn.csproj | 23 - .../Api.ApiClients.RootLogIn.Models.csproj | 75 -- .../Api.ApiClients.RootLogIn.sln | 159 ---- ...ApiClients.RootLogin.Service.Models.csproj | 75 -- .../ApiClient/NanoApiClient.cs | 24 - .../Requests/AutoAuthenticateRootRequest.cs | 9 - .../ApiClient/Requests/BaseCustomsRequest.cs | 17 - .../Api.ApiClients.RootLogin.Service.csproj | 37 - .../Controllers/AuthController.cs | 10 - .../Controllers/CustomsController.cs | 39 - .../Dockerfile.Local | 6 - .../Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 26 - .../Api.ApiClients.RootLogIn.csproj | 38 - .../Controllers/ExamplesController.cs | 41 - .../Api.ApiClients.RootLogin/Dockerfile.Local | 6 - .../Api.ApiClients.RootLogin/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.ApiClients.RootLogin/appsettings.json | 30 - Api.ApiClients.RootLogIn/Dockerfile | 35 - Api.ApiClients.RootLogIn/LICENSE | 18 - Api.ApiClients.RootLogIn/README.md | 31 - Api.ApiClients.RootLogIn/icon.png | Bin 14103 -> 0 bytes Api.ApiClients/.docker/docker-compose.dcproj | 13 - Api.ApiClients/.docker/docker-compose.yml | 29 - Api.ApiClients/.dockerignore | 12 - Api.ApiClients/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.ApiClients/.gitignore | 11 - Api.ApiClients/.kubernetes/autoscaler.yaml | 25 - Api.ApiClients/.kubernetes/configmap.yaml | 8 - Api.ApiClients/.kubernetes/deployment.yaml | 83 -- Api.ApiClients/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.ApiClients.csproj | 23 - .../Api.ApiClients.Models.csproj | 75 -- .../Api.ApiClients.Service.Models.csproj | 75 -- .../Api/NanoApiClient.cs | 92 -- .../Requests/BadRequestExceptionRequest.cs | 9 - .../Api/Requests/BaseCustomsRequest.cs | 17 - .../Api/Requests/CustomFileBodyRequest.cs | 25 - .../Api/Requests/CustomFileRequest.cs | 18 - .../Api/Requests/CustomRequest.cs | 43 - .../Api/Requests/Models/CustomBody.cs | 12 - .../Api/Requests/Models/CustomFileBody.cs | 15 - .../ProblemDetailsExceptionRequest.cs | 9 - .../Api/Requests/RequestTracingRequest.cs | 9 - .../Api/Responses/CustomFileBodyResponse.cs | 14 - .../Api/Responses/CustomFileResponse.cs | 12 - .../Api/Responses/CustomResponse.cs | 36 - .../Api/Responses/RequestTracingResponse.cs | 12 - .../Api.ApiClients.Service.csproj | 37 - .../Controllers/CustomsController.cs | 158 ---- .../Api.ApiClients.Service/Dockerfile.Local | 6 - .../Api.ApiClients.Service/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.ApiClients.Service/appsettings.json | 13 - Api.ApiClients/Api.ApiClients.sln | 159 ---- .../Api.ApiClients/Api.ApiClients.csproj | 38 - .../Controllers/ExamplesController.cs | 184 ---- .../Api.ApiClients/Dockerfile.Local | 6 - Api.ApiClients/Api.ApiClients/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Api.ApiClients/appsettings.Staging.json | 2 - .../Api.ApiClients/appsettings.json | 26 - Api.ApiClients/Dockerfile | 35 - Api.ApiClients/LICENSE | 18 - Api.ApiClients/README.md | 64 -- Api.ApiClients/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.Auth.External.Custom/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 179 ---- Api.Auth.External.Custom/.gitignore | 10 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 94 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Auth.External.Custom.csproj | 23 - .../Api.Auth.External.Custom.Models.csproj | 75 -- .../Api.Auth.External.Custom.sln | 133 --- .../Api.Auth.External.Custom.csproj | 37 - .../ExternalProviderCustomRepository.cs | 44 - .../Controllers/AuthController.cs | 10 - .../Controllers/ExamplesController.cs | 31 - .../Api.Auth.External.Custom/Dockerfile.Local | 6 - .../Api.Auth.External.Custom/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 22 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Auth.External.Custom/appsettings.json | 25 - Api.Auth.External.Custom/Dockerfile | 35 - Api.Auth.External.Custom/LICENSE | 18 - Api.Auth.External.Custom/README.md | 124 --- Api.Auth.External.Custom/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Auth.RootLogin/.docker/docker-compose.yml | 17 - Api.Auth.RootLogin/.dockerignore | 12 - Api.Auth.RootLogin/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 179 ---- Api.Auth.RootLogin/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - Api.Auth.RootLogin/.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 94 -- Api.Auth.RootLogin/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Auth.RootLogin.csproj | 23 - .../Api.Auth.RootLogin.Models.csproj | 75 -- Api.Auth.RootLogin/Api.Auth.RootLogin.sln | 133 --- .../Api.Auth.RootLogin.csproj | 37 - .../Controllers/AuthController.cs | 10 - .../Controllers/ExamplesController.cs | 31 - .../Api.Auth.RootLogin/Dockerfile.Local | 6 - .../Api.Auth.RootLogin/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 17 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Auth.RootLogin/appsettings.json | 25 - Api.Auth.RootLogin/Dockerfile | 35 - Api.Auth.RootLogin/LICENSE | 18 - Api.Auth.RootLogin/README.md | 124 --- Api.Auth.RootLogin/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Authorization/.docker/docker-compose.yml | 17 - Api.Authorization/.dockerignore | 12 - Api.Authorization/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 179 ---- Api.Authorization/.gitignore | 11 - Api.Authorization/.kubernetes/autoscaler.yaml | 25 - Api.Authorization/.kubernetes/configmap.yaml | 8 - Api.Authorization/.kubernetes/deployment.yaml | 94 -- Api.Authorization/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Authorization.csproj | 23 - .../Api.Authorization.Models.csproj | 75 -- Api.Authorization/Api.Authorization.sln | 133 --- .../Api.Authorization.csproj | 37 - .../Controllers/AuthController.cs | 10 - .../Controllers/ExamplesController.cs | 49 - .../Api.Authorization/Dockerfile.Local | 6 - .../Extensions/ServiceCollectionExtensions.cs | 20 - .../Api.Authorization/Program.cs | 11 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 26 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Authorization/appsettings.json | 25 - Api.Authorization/Dockerfile | 35 - Api.Authorization/LICENSE | 18 - Api.Authorization/README.md | 46 - Api.Authorization/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.ContentNegotiation/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.ContentNegotiation/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.ContentNegotiation.csproj | 23 - .../Api.ContentNegotiation.Models.csproj | 75 -- .../Api.ContentNegotiation.sln | 133 --- .../Api.ContentNegotiation.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.ContentNegotiation/Dockerfile.Local | 6 - .../Api.ContentNegotiation/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.ContentNegotiation/appsettings.json | 13 - Api.ContentNegotiation/Dockerfile | 35 - Api.ContentNegotiation/LICENSE | 18 - Api.ContentNegotiation/README.md | 30 - Api.ContentNegotiation/icon.png | Bin 14103 -> 0 bytes Api.Cookies/.docker/docker-compose.dcproj | 13 - Api.Cookies/.docker/docker-compose.yml | 17 - Api.Cookies/.dockerignore | 12 - Api.Cookies/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Cookies/.gitignore | 11 - Api.Cookies/.kubernetes/autoscaler.yaml | 25 - Api.Cookies/.kubernetes/configmap.yaml | 8 - Api.Cookies/.kubernetes/deployment.yaml | 83 -- Api.Cookies/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Cookies.csproj | 23 - .../Api.Cookies.Models.csproj | 75 -- Api.Cookies/Api.Cookies.sln | 133 --- Api.Cookies/Api.Cookies/Api.Cookies.csproj | 37 - .../Controllers/ExamplesController.cs | 91 -- Api.Cookies/Api.Cookies/Dockerfile.Local | 6 - Api.Cookies/Api.Cookies/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Api.Cookies/appsettings.Development.json | 2 - .../Api.Cookies/appsettings.Production.json | 2 - .../Api.Cookies/appsettings.Staging.json | 2 - Api.Cookies/Api.Cookies/appsettings.json | 13 - Api.Cookies/Dockerfile | 35 - Api.Cookies/LICENSE | 18 - Api.Cookies/README.md | 33 - Api.Cookies/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.CustomConfigSection/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.CustomConfigSection/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.CustomConfigSection.csproj | 23 - .../Api.CustomConfigSection.Models.csproj | 75 -- .../Api.CustomConfigSection.sln | 133 --- .../Api.CustomConfigSection.csproj | 37 - .../Config/CustomOptions.cs | 17 - .../Controllers/ExamplesController.cs | 37 - .../Api.CustomConfigSection/Dockerfile.Local | 6 - .../Api.CustomConfigSection/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.CustomConfigSection/appsettings.json | 16 - Api.CustomConfigSection/Dockerfile | 35 - Api.CustomConfigSection/LICENSE | 18 - Api.CustomConfigSection/README.md | 65 -- Api.CustomConfigSection/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.CustomMiddleware/.dockerignore | 12 - Api.CustomMiddleware/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.CustomMiddleware/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- Api.CustomMiddleware/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.CustomMiddleware.csproj | 23 - .../Api.CustomMiddleware.Models.csproj | 75 -- Api.CustomMiddleware/Api.CustomMiddleware.sln | 133 --- .../Api.CustomMiddleware.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.CustomMiddleware/Dockerfile.Local | 6 - .../Api.CustomMiddleware/Program.cs | 19 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.CustomMiddleware/appsettings.json | 13 - Api.CustomMiddleware/Dockerfile | 35 - Api.CustomMiddleware/LICENSE | 18 - Api.CustomMiddleware/README.md | 49 - Api.CustomMiddleware/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.CustomService/.docker/docker-compose.yml | 17 - Api.CustomService/.dockerignore | 12 - Api.CustomService/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.CustomService/.gitignore | 11 - Api.CustomService/.kubernetes/autoscaler.yaml | 25 - Api.CustomService/.kubernetes/configmap.yaml | 8 - Api.CustomService/.kubernetes/deployment.yaml | 83 -- Api.CustomService/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.CustomService.csproj | 23 - .../Api.CustomService.Models.csproj | 75 -- Api.CustomService/Api.CustomService.sln | 133 --- .../Api.CustomService.csproj | 37 - .../Controllers/ExamplesController.cs | 37 - .../Api.CustomService/Dockerfile.Local | 6 - .../Api.CustomService/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../Services/Abstractions/IExampleService.cs | 15 - .../Services/ExampleService.cs | 16 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.CustomService/appsettings.json | 13 - Api.CustomService/Dockerfile | 35 - Api.CustomService/LICENSE | 18 - Api.CustomService/README.md | 44 - Api.CustomService/icon.png | Bin 14103 -> 0 bytes Api.Data.Audit/.docker/docker-compose.dcproj | 13 - Api.Data.Audit/.docker/docker-compose.yml | 32 - Api.Data.Audit/.dockerignore | 12 - Api.Data.Audit/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.Audit/.gitignore | 11 - Api.Data.Audit/.kubernetes/autoscaler.yaml | 25 - Api.Data.Audit/.kubernetes/configmap.yaml | 8 - Api.Data.Audit/.kubernetes/deployment.yaml | 89 -- Api.Data.Audit/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.Audit.csproj | 23 - .../Api.Data.Audit.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Api.Data.Audit.Models/Example.cs | 28 - .../ExampleNavigation.cs | 21 - .../Api.Data.Audit.Models/ExampleNoAudit.cs | 14 - Api.Data.Audit/Api.Data.Audit.sln | 147 --- .../Api.Data.Audit/Api.Data.Audit.csproj | 37 - .../Controllers/AuditController.cs | 9 - .../Controllers/ExampleNoAuditsController.cs | 15 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 34 - .../Data/Mappings/ExampleNavigationMapping.cs | 27 - .../Data/Mappings/ExampleNoAuditMapping.cs | 23 - .../Api.Data.Audit/Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.Audit/Dockerfile.Local | 6 - .../20260415133755_Initial.Designer.cs | 728 --------------- .../Migrations/20260415133755_Initial.cs | 667 -------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 725 --------------- Api.Data.Audit/Api.Data.Audit/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../Api.Data.Audit/appsettings.Staging.json | 2 - .../Api.Data.Audit/appsettings.json | 18 - Api.Data.Audit/Dockerfile | 35 - Api.Data.Audit/LICENSE | 18 - Api.Data.Audit/README.md | 37 - Api.Data.Audit/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 62 -- Api.Data.EntityEvents/.docker/init.sql | 9 - Api.Data.EntityEvents/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.EntityEvents/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 94 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.EntityEvents.csproj | 23 - .../Api.Data.EntityEvents.Models/Address.cs | 21 - .../Api.Data.EntityEvents.Models.csproj | 79 -- .../Criterias/DefaultQueryCriteria.cs | 17 - .../Api.Data.EntityEvents.Models/Customer.cs | 43 - .../Api.Data.EntityEvents.Models/Order.cs | 22 - .../Owned/ProfileSettings.cs | 15 - .../Api.Data.EntityEvents.Models/Person.cs | 18 - .../Api.Data.EntityEvents.Models/Profile.cs | 35 - ...Data.EntityEvents.Subscriber.Models.csproj | 79 -- .../Customer.cs | 47 - .../Api.Data.EntityEvents.Subscriber.csproj | 37 - .../Data/Mappings/CustomerMapping.cs | 41 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260414100646_Initial.Designer.cs | 665 -------------- .../Migrations/20260414100646_Initial.cs | 603 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 662 -------------- .../Program.cs | 16 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 23 - .../Api.Data.EntityEvents.sln | 177 ---- .../Api.Data.EntityEvents.csproj | 37 - .../Controllers/AddresssController.cs | 15 - .../Controllers/CustomersController.cs | 15 - .../Controllers/OrdersController.cs | 15 - .../Controllers/ProfilesController.cs | 15 - .../Data/Mappings/AddressMapping.cs | 26 - .../Data/Mappings/CustomerMapping.cs | 48 - .../Data/Mappings/OrderMapping.cs | 23 - .../Data/Mappings/PersonMapping.cs | 23 - .../Data/Mappings/ProfileMapping.cs | 32 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.EntityEvents/Dockerfile.Local | 6 - .../20260412140549_Initial.Designer.cs | 849 ------------------ .../Migrations/20260412140549_Initial.cs | 756 ---------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 846 ----------------- .../Api.Data.EntityEvents/Program.cs | 16 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.EntityEvents/appsettings.json | 23 - Api.Data.EntityEvents/Dockerfile | 35 - Api.Data.EntityEvents/LICENSE | 18 - Api.Data.EntityEvents/README.md | 43 - Api.Data.EntityEvents/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.Identity.Auth.ApiKey/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 226 ----- Api.Data.Identity.Auth.ApiKey/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 104 --- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ...Tests.Api.Data.Identity.Auth.ApiKey.csproj | 23 - ...pi.Data.Identity.Auth.ApiKey.Models.csproj | 77 -- .../Criterias/UserQueryCriteria.cs | 34 - .../User.cs | 17 - .../Api.Data.Identity.Auth.ApiKey.sln | 147 --- .../Api.Data.Identity.Auth.ApiKey.csproj | 37 - .../Controllers/AuthController.cs | 9 - .../Controllers/UsersController.cs | 12 - .../Data/Mappings/UserMapping.cs | 24 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260415143026_Initial.Designer.cs | 660 -------------- .../Migrations/20260415143026_Initial.cs | 601 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 657 -------------- .../Api.Data.Identity.Auth.ApiKey/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 18 - .../appsettings.Production.json | 10 - .../appsettings.Staging.json | 10 - .../appsettings.json | 60 -- Api.Data.Identity.Auth.ApiKey/Dockerfile | 35 - Api.Data.Identity.Auth.ApiKey/LICENSE | 18 - Api.Data.Identity.Auth.ApiKey/README.md | 94 -- Api.Data.Identity.Auth.ApiKey/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - .../.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 219 ----- .../.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 99 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ....Data.Identity.Auth.External.Custom.csproj | 23 - ...dentity.Auth.External.Custom.Models.csproj | 77 -- .../Criterias/UserQueryCriteria.cs | 34 - .../User.cs | 17 - ...Api.Data.Identity.Auth.External.Custom.sln | 147 --- ....Data.Identity.Auth.External.Custom.csproj | 37 - .../ExternalProviderCustomRepository.cs | 44 - .../Controllers/AuthController.cs | 9 - .../Controllers/UsersController.cs | 12 - .../Data/Mappings/UserMapping.cs | 24 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260415143439_Initial.Designer.cs | 660 -------------- .../Migrations/20260415143439_Initial.cs | 601 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 657 -------------- .../Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 18 - .../appsettings.Production.json | 10 - .../appsettings.Staging.json | 10 - .../appsettings.json | 57 -- .../Dockerfile | 35 - .../LICENSE | 18 - .../README.md | 86 -- .../icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.Identity.Auth.Jwt/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 219 ----- Api.Data.Identity.Auth.Jwt/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 99 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.Identity.Auth.Jwt.csproj | 23 - .../Api.Data.Identity.Auth.Jwt.Models.csproj | 77 -- .../Criterias/UserQueryCriteria.cs | 34 - .../Api.Data.Identity.Auth.Jwt.Models/User.cs | 17 - .../Api.Data.Identity.Auth.Jwt.sln | 147 --- .../Api.Data.Identity.Auth.Jwt.csproj | 37 - .../Controllers/AuthController.cs | 9 - .../Controllers/UsersController.cs | 12 - .../Data/Mappings/UserMapping.cs | 24 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260415143540_Initial.Designer.cs | 660 -------------- .../Migrations/20260415143540_Initial.cs | 601 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 657 -------------- .../Api.Data.Identity.Auth.Jwt/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 18 - .../appsettings.Production.json | 10 - .../appsettings.Staging.json | 10 - .../appsettings.json | 57 -- Api.Data.Identity.Auth.Jwt/Dockerfile | 35 - Api.Data.Identity.Auth.Jwt/LICENSE | 18 - Api.Data.Identity.Auth.Jwt/README.md | 118 --- Api.Data.Identity.Auth.Jwt/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Data.Identity/.docker/docker-compose.yml | 32 - Api.Data.Identity/.dockerignore | 12 - Api.Data.Identity/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.Identity/.gitignore | 11 - Api.Data.Identity/.kubernetes/autoscaler.yaml | 25 - Api.Data.Identity/.kubernetes/configmap.yaml | 8 - Api.Data.Identity/.kubernetes/deployment.yaml | 89 -- Api.Data.Identity/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.Identity.csproj | 23 - .../Api.Data.Identity.Models.csproj | 77 -- .../Criterias/UserQueryCriteria.cs | 34 - .../Api.Data.Identity.Models/User.cs | 17 - Api.Data.Identity/Api.Data.Identity.sln | 147 --- .../Api.Data.Identity.csproj | 37 - .../Controllers/UsersController.cs | 12 - .../Data/Mappings/UserMapping.cs | 24 - .../Api.Data.Identity/Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.Identity/Dockerfile.Local | 6 - .../20260415142847_Initial.Designer.cs | 660 -------------- .../Migrations/20260415142847_Initial.cs | 601 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 657 -------------- .../Api.Data.Identity/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.Identity/appsettings.json | 47 - Api.Data.Identity/Dockerfile | 35 - Api.Data.Identity/LICENSE | 18 - Api.Data.Identity/README.md | 75 -- Api.Data.Identity/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Data.InMemory/.docker/docker-compose.yml | 17 - Api.Data.InMemory/.dockerignore | 12 - Api.Data.InMemory/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Data.InMemory/.gitignore | 11 - Api.Data.InMemory/.kubernetes/autoscaler.yaml | 25 - Api.Data.InMemory/.kubernetes/configmap.yaml | 8 - Api.Data.InMemory/.kubernetes/deployment.yaml | 83 -- Api.Data.InMemory/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.InMemory.csproj | 23 - .../Api.Data.InMemory.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Api.Data.InMemory.Models/Example.cs | 14 - .../ExampleCreatable.cs | 14 - .../ExampleCreatableAndEditable.cs | 14 - .../ExampleDeletable.cs | 14 - .../ExampleUpdatable.cs | 14 - Api.Data.InMemory/Api.Data.InMemory.sln | 147 --- .../Api.Data.InMemory.csproj | 37 - .../ExampleCreatableAndEditableController.cs | 15 - .../ExampleCreateablesController.cs | 15 - .../ExampleDeletablesController.cs | 15 - .../ExampleUpdatablesController.cs | 15 - .../Controllers/ExamplesController.cs | 15 - .../Data/InMemoryDbContext.cs | 11 - .../ExampleCreatableAndUpdatableMapping.cs | 26 - .../Data/Mappings/ExampleCreatableMapping.cs | 26 - .../Data/Mappings/ExampleDeletableMapping.cs | 26 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/Mappings/ExampleUpdatableMapping.cs | 26 - .../Api.Data.InMemory/Dockerfile.Local | 6 - .../Api.Data.InMemory/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.InMemory/appsettings.json | 37 - Api.Data.InMemory/Dockerfile | 35 - Api.Data.InMemory/LICENSE | 18 - Api.Data.InMemory/README.md | 78 -- Api.Data.InMemory/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.LazyLoading/.dockerignore | 12 - Api.Data.LazyLoading/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.LazyLoading/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- Api.Data.LazyLoading/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.LazyLoading.csproj | 23 - .../Api.Data.LazyLoading.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 17 - .../Api.Data.LazyLoading.Models/Example.cs | 27 - .../ExampleRelation.cs | 27 - .../ExampleRelationIncluded.cs | 27 - Api.Data.LazyLoading/Api.Data.LazyLoading.sln | 147 --- .../Api.Data.LazyLoading.csproj | 37 - .../Controllers/ExamplesController.cs | 45 - .../Data/Mappings/ExampleMapping.cs | 36 - .../ExampleRelationIncludedMapping.cs | 28 - .../Data/Mappings/ExampleRelationMapping.cs | 28 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.LazyLoading/Dockerfile.Local | 6 - .../20260415143541_Initial.Designer.cs | 748 --------------- .../Migrations/20260415143541_Initial.cs | 685 -------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 745 --------------- .../Api.Data.LazyLoading/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.LazyLoading/appsettings.json | 17 - Api.Data.LazyLoading/Dockerfile | 35 - Api.Data.LazyLoading/LICENSE | 18 - Api.Data.LazyLoading/README.md | 33 - Api.Data.LazyLoading/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.MySql.Collation/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.MySql.Collation/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.MySql.Collation.csproj | 23 - .../Api.Data.MySql.Collation.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Example.cs | 14 - .../Api.Data.MySql.Collation.sln | 147 --- .../Api.Data.MySql.Collation.csproj | 37 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/MySqlDbContext.cs | 11 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.MySql.Collation/Dockerfile.Local | 6 - .../20260415143932_Initial.Designer.cs | 651 -------------- .../Migrations/20260415143932_Initial.cs | 601 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 648 ------------- .../Api.Data.MySql.Collation/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.MySql.Collation/appsettings.json | 16 - Api.Data.MySql.Collation/Dockerfile | 35 - Api.Data.MySql.Collation/LICENSE | 18 - Api.Data.MySql.Collation/README.md | 37 - Api.Data.MySql.Collation/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.MySql.Mappings/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.MySql.Mappings/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.MySql.Mappings.csproj | 23 - .../Api.Data.MySql.Mappings.Models.csproj | 77 -- .../ExampleNormalizedQueryCriteria.cs | 34 - .../Criterias/ExampleQueryCriteria.cs | 17 - .../ExampleJson.cs | 17 - .../ExampleNormalized.cs | 65 -- .../ExampleOwned.cs | 17 - .../Types/Profile.cs | 25 - .../Types/ProfilePicture.cs | 19 - .../Types/ProfileSettings.cs | 24 - .../Api.Data.MySql.Mappings.sln | 147 --- .../Api.Data.MySql.Mappings.csproj | 37 - .../Controllers/ExampleJsonController.cs | 15 - .../ExampleNormalizedController.cs | 15 - .../Controllers/ExampleOwnedController.cs | 15 - .../Data/Mappings/ExampleJsonMapping.cs | 28 - .../Data/Mappings/ExampleNormalizedMapping.cs | 44 - .../Data/Mappings/ExampleOwnedMapping.cs | 24 - .../Extensions/EntityTypeBuilderExtensions.cs | 41 - .../OwnedNavigationBuilderExtensions.cs | 70 -- .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.MySql.Mappings/Dockerfile.Local | 6 - .../20260415143933_Initial.Designer.cs | 794 ---------------- .../Migrations/20260415143933_Initial.cs | 673 -------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 791 ---------------- .../Api.Data.MySql.Mappings/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.MySql.Mappings/appsettings.json | 16 - Api.Data.MySql.Mappings/Dockerfile | 35 - Api.Data.MySql.Mappings/LICENSE | 18 - Api.Data.MySql.Mappings/README.md | 30 - Api.Data.MySql.Mappings/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.MySql.Spatial/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.MySql.Spatial/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.MySql.Spatial.csproj | 23 - .../Api.Data.MySql.Spatial.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 46 - .../Api.Data.MySql.Spatial.Models/Example.cs | 22 - .../Api.Data.MySql.Spatial.sln | 147 --- .../Api.Data.MySql.Spatial.csproj | 37 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 39 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.MySql.Spatial/Dockerfile.Local | 6 - .../20260415143931_Initial.Designer.cs | 662 -------------- .../Migrations/20260415143931_Initial.cs | 610 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 659 -------------- .../Api.Data.MySql.Spatial/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.MySql.Spatial/appsettings.json | 16 - Api.Data.MySql.Spatial/Dockerfile | 35 - Api.Data.MySql.Spatial/LICENSE | 18 - Api.Data.MySql.Spatial/README.md | 25 - Api.Data.MySql.Spatial/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.MySql.StoredProcedures/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.MySql.StoredProcedures/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ...sts.Api.Data.MySql.StoredProcedures.csproj | 23 - ....Data.MySql.StoredProcedures.Models.csproj | 77 -- .../Example.cs | 23 - .../ExampleResult.cs | 26 - .../Api.Data.MySql.StoredProcedures.sln | 147 --- .../Api.Data.MySql.StoredProcedures.csproj | 37 - .../Controllers/ExamplesController.cs | 38 - .../Data/Extensions/RepositoryExtensions.cs | 28 - .../Data/Mappings/ExampleMapping.cs | 32 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Data/StoredProcedures/StoredProcedures.cs | 28 - .../Dockerfile.Local | 6 - .../20260415151833_Initial.Designer.cs | 656 -------------- .../Migrations/20260415151833_Initial.cs | 602 ------------- ...415151941_AddedStoredProcedure.Designer.cs | 656 -------------- .../20260415151941_AddedStoredProcedure.cs | 24 - .../Migrations/MySqlDbContextModelSnapshot.cs | 653 -------------- .../Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 16 - Api.Data.MySql.StoredProcedures/Dockerfile | 35 - Api.Data.MySql.StoredProcedures/LICENSE | 18 - Api.Data.MySql.StoredProcedures/README.md | 38 - Api.Data.MySql.StoredProcedures/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.MySql.Views/.dockerignore | 12 - Api.Data.MySql.Views/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.MySql.Views/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- Api.Data.MySql.Views/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.MySql.Views.csproj | 23 - .../Api.Data.MySql.Views.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Api.Data.MySql.Views.Models/Example.cs | 14 - .../ExampleView.cs | 30 - Api.Data.MySql.Views/Api.Data.MySql.Views.sln | 147 --- .../Api.Data.MySql.Views.csproj | 37 - .../Controllers/ExampleViewsController.cs | 15 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 23 - .../Data/Mappings/ExampleViewMapping.cs | 32 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Data/Views/ExampleViewDefinition.cs | 15 - .../Api.Data.MySql.Views/Dockerfile.Local | 6 - .../20260415152009_Initial.Designer.cs | 669 -------------- .../Migrations/20260415152009_Initial.cs | 596 ------------ .../20260415152149_AddedView.Designer.cs | 669 -------------- .../Migrations/20260415152149_AddedView.cs | 24 - .../Migrations/MySqlDbContextModelSnapshot.cs | 666 -------------- .../Api.Data.MySql.Views/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.MySql.Views/appsettings.json | 16 - Api.Data.MySql.Views/Dockerfile | 35 - Api.Data.MySql.Views/LICENSE | 18 - Api.Data.MySql.Views/README.md | 32 - Api.Data.MySql.Views/icon.png | Bin 14103 -> 0 bytes Api.Data.MySql/.docker/docker-compose.dcproj | 13 - Api.Data.MySql/.docker/docker-compose.yml | 32 - Api.Data.MySql/.dockerignore | 12 - Api.Data.MySql/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.MySql/.gitignore | 11 - Api.Data.MySql/.kubernetes/autoscaler.yaml | 25 - Api.Data.MySql/.kubernetes/configmap.yaml | 8 - Api.Data.MySql/.kubernetes/deployment.yaml | 89 -- Api.Data.MySql/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.MySql.csproj | 23 - .../Api.Data.MySql.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Api.Data.MySql.Models/Example.cs | 14 - .../Api.Data.MySql.Models/ExampleCreatable.cs | 14 - .../ExampleCreatableAndEditable.cs | 14 - .../Api.Data.MySql.Models/ExampleDeletable.cs | 14 - .../Api.Data.MySql.Models/ExampleUpdatable.cs | 14 - Api.Data.MySql/Api.Data.MySql.sln | 147 --- .../Api.Data.MySql/Api.Data.MySql.csproj | 37 - .../ExampleCreatableAndEditableController.cs | 15 - .../ExampleCreateablesController.cs | 15 - .../ExampleDeletablesController.cs | 15 - .../ExampleUpdatablesController.cs | 15 - .../Controllers/ExamplesController.cs | 15 - .../ExampleCreatableAndUpdatableMapping.cs | 26 - .../Data/Mappings/ExampleCreatableMapping.cs | 26 - .../Data/Mappings/ExampleDeletableMapping.cs | 26 - .../Data/Mappings/ExampleMapping.cs | 27 - .../Data/Mappings/ExampleUpdatableMapping.cs | 26 - .../Api.Data.MySql/Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.MySql/Dockerfile.Local | 6 - .../20260423071339_Initial.Designer.cs | 781 ---------------- .../Migrations/20260423071339_Initial.cs | 742 --------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 778 ---------------- Api.Data.MySql/Api.Data.MySql/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../Api.Data.MySql/appsettings.Staging.json | 2 - .../Api.Data.MySql/appsettings.json | 39 - Api.Data.MySql/Dockerfile | 35 - Api.Data.MySql/LICENSE | 18 - Api.Data.MySql/README.md | 196 ---- Api.Data.MySql/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 30 - Api.Data.PostgreSQL.Spatial/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 228 ----- Api.Data.PostgreSQL.Spatial/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.PostgreSQL.Spatial.csproj | 23 - .../Api.Data.PostgreSQL.Spatial.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 46 - .../Example.cs | 22 - .../Api.Data.PostgreSQL.Spatial.sln | 147 --- .../Api.Data.PostgreSQL.Spatial.csproj | 37 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 38 - .../Data/PostgreSqlDbContext.cs | 10 - .../Data/PostgreSqlDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260415150018_Initial.Designer.cs | 661 -------------- .../Migrations/20260415150018_Initial.cs | 552 ------------ .../PostgreSqlDbContextModelSnapshot.cs | 658 -------------- .../Api.Data.PostgreSQL.Spatial/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 16 - Api.Data.PostgreSQL.Spatial/Dockerfile | 35 - Api.Data.PostgreSQL.Spatial/LICENSE | 18 - Api.Data.PostgreSQL.Spatial/README.md | 25 - Api.Data.PostgreSQL.Spatial/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 30 - Api.Data.PostgreSQL/.dockerignore | 12 - Api.Data.PostgreSQL/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 228 ----- Api.Data.PostgreSQL/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- Api.Data.PostgreSQL/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.PostgreSQL.csproj | 23 - .../Api.Data.PostgreSQL.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Api.Data.PostgreSQL.Models/Example.cs | 14 - .../ExampleCreatable.cs | 14 - .../ExampleCreatableAndEditable.cs | 14 - .../ExampleDeletable.cs | 14 - .../ExampleUpdatable.cs | 14 - Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln | 147 --- .../Api.Data.PostgreSQL.csproj | 37 - .../ExampleCreatableAndEditableController.cs | 15 - .../ExampleCreateablesController.cs | 15 - .../ExampleDeletablesController.cs | 15 - .../ExampleUpdatablesController.cs | 15 - .../Controllers/ExamplesController.cs | 15 - .../ExampleCreatableAndUpdatableMapping.cs | 26 - .../Data/Mappings/ExampleCreatableMapping.cs | 26 - .../Data/Mappings/ExampleDeletableMapping.cs | 26 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/Mappings/ExampleUpdatableMapping.cs | 26 - .../Data/PostgreSqlDbContext.cs | 10 - .../Data/PostgreSqlDbContextFactory.cs | 7 - .../Api.Data.PostgreSQL/Dockerfile.Local | 6 - .../20260415145934_Initial.Designer.cs | 770 ---------------- .../Migrations/20260415145934_Initial.cs | 672 -------------- .../PostgreSqlDbContextModelSnapshot.cs | 767 ---------------- .../Api.Data.PostgreSQL/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.PostgreSQL/appsettings.json | 42 - Api.Data.PostgreSQL/Dockerfile | 35 - Api.Data.PostgreSQL/LICENSE | 18 - Api.Data.PostgreSQL/README.md | 211 ----- Api.Data.PostgreSQL/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.Repository.AutoSave/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.Repository.AutoSave/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.Repository.AutoSave.csproj | 23 - ...Api.Data.Repository.AutoSave.Models.csproj | 77 -- .../Example.cs | 14 - .../Api.Data.Repository.AutoSave.sln | 147 --- .../Api.Data.Repository.AutoSave.csproj | 37 - .../Controllers/ExamplesController.cs | 39 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260415150030_Initial.Designer.cs | 651 -------------- .../Migrations/20260415150030_Initial.cs | 601 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 648 ------------- .../Api.Data.Repository.AutoSave/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 19 - Api.Data.Repository.AutoSave/Dockerfile | 35 - Api.Data.Repository.AutoSave/LICENSE | 18 - Api.Data.Repository.AutoSave/README.md | 44 - Api.Data.Repository.AutoSave/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.Repository.Includes/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.Repository.Includes/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.Repository.Includes.csproj | 23 - ...Api.Data.Repository.Includes.Models.csproj | 77 -- .../Customer.cs | 33 - .../CustomerProfile.cs | 21 - .../Order.cs | 34 - .../Payment.cs | 14 - .../Api.Data.Repository.Includes.sln | 147 --- .../Api.Data.Repository.Includes.csproj | 37 - .../Controllers/ExamplesController.cs | 146 --- .../Response/CustomerProfileResponse.cs | 19 - .../Controllers/Response/CustomerResponse.cs | 25 - .../Controllers/Response/OrderResponse.cs | 24 - .../Controllers/Response/PaymentResponse.cs | 14 - .../Data/Mappings/CustomerMapping.cs | 30 - .../Data/Mappings/CustomerProfileMapping.cs | 28 - .../Data/Mappings/OrderMapping.cs | 29 - .../Data/Mappings/PaymentMapping.cs | 24 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260415150006_Initial.Designer.cs | 791 ---------------- .../Migrations/20260415150006_Initial.cs | 717 --------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 788 ---------------- .../Api.Data.Repository.Includes/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 19 - Api.Data.Repository.Includes/Dockerfile | 35 - Api.Data.Repository.Includes/LICENSE | 18 - Api.Data.Repository.Includes/README.md | 55 -- Api.Data.Repository.Includes/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 32 - Api.Data.SoftDelete/.dockerignore | 12 - Api.Data.SoftDelete/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.SoftDelete/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- Api.Data.SoftDelete/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.SoftDelete.csproj | 23 - .../Api.Data.SoftDelete.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Api.Data.SoftDelete.Models/Example.cs | 15 - Api.Data.SoftDelete/Api.Data.SoftDelete.sln | 147 --- .../Api.Data.SoftDelete.csproj | 37 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 39 - .../Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.SoftDelete/Dockerfile.Local | 6 - .../20260415151003_Initial.Designer.cs | 651 -------------- .../Migrations/20260415151003_Initial.cs | 601 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 648 ------------- .../Api.Data.SoftDelete/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.SoftDelete/appsettings.json | 16 - Api.Data.SoftDelete/Dockerfile | 35 - Api.Data.SoftDelete/LICENSE | 18 - Api.Data.SoftDelete/README.md | 29 - Api.Data.SoftDelete/icon.png | Bin 14103 -> 0 bytes Api.Data.SqLite/.docker/docker-compose.dcproj | 13 - Api.Data.SqLite/.docker/docker-compose.yml | 19 - Api.Data.SqLite/.dockerignore | 12 - Api.Data.SqLite/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 201 ----- Api.Data.SqLite/.gitignore | 11 - Api.Data.SqLite/.kubernetes/autoscaler.yaml | 25 - Api.Data.SqLite/.kubernetes/configmap.yaml | 8 - Api.Data.SqLite/.kubernetes/data-pvc.yaml | 11 - .../.kubernetes/data-storageclass.yaml | 10 - Api.Data.SqLite/.kubernetes/deployment.yaml | 85 -- Api.Data.SqLite/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.SqLite.csproj | 23 - .../Api.Data.SqLite.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Api.Data.SqLite.Models/Example.cs | 14 - .../ExampleCreatable.cs | 14 - .../ExampleCreatableAndEditable.cs | 14 - .../ExampleDeletable.cs | 14 - .../ExampleUpdatable.cs | 14 - Api.Data.SqLite/Api.Data.SqLite.sln | 149 --- .../Api.Data.SqLite/Api.Data.SqLite.csproj | 37 - .../ExampleCreatableAndEditableController.cs | 15 - .../ExampleCreateablesController.cs | 15 - .../ExampleDeletablesController.cs | 15 - .../ExampleUpdatablesController.cs | 15 - .../Controllers/ExamplesController.cs | 15 - .../ExampleCreatableAndUpdatableMapping.cs | 26 - .../Data/Mappings/ExampleCreatableMapping.cs | 26 - .../Data/Mappings/ExampleDeletableMapping.cs | 26 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/Mappings/ExampleUpdatableMapping.cs | 26 - .../Data/MySqlDbContextFactory.cs | 7 - .../Api.Data.SqLite/Data/SqLiteDbContext.cs | 10 - .../Api.Data.SqLite/Dockerfile.Local | 10 - .../20260415151010_Initial.Designer.cs | 758 ---------------- .../Migrations/20260415151010_Initial.cs | 668 -------------- .../SqLiteDbContextModelSnapshot.cs | 755 ---------------- Api.Data.SqLite/Api.Data.SqLite/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 5 - .../appsettings.Production.json | 2 - .../Api.Data.SqLite/appsettings.Staging.json | 2 - .../Api.Data.SqLite/appsettings.json | 42 - Api.Data.SqLite/Dockerfile | 39 - Api.Data.SqLite/LICENSE | 18 - Api.Data.SqLite/README.md | 175 ---- Api.Data.SqLite/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 30 - Api.Data.SqlServer.Spatial/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 239 ----- Api.Data.SqlServer.Spatial/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.SqlServer.Spatial.csproj | 23 - .../Api.Data.SqlServer.Spatial.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 46 - .../Example.cs | 22 - .../Api.Data.SqlServer.Spatial.sln | 147 --- .../Api.Data.SqlServer.Spatial.csproj | 37 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 32 - .../Data/SqlServerDbContext.cs | 10 - .../Data/SqlServerDbContextFactory.cs | 7 - .../Dockerfile.Local | 6 - .../20260415151456_Initial.Designer.cs | 658 -------------- .../Migrations/20260415151456_Initial.cs | 546 ----------- ...260415151538_AddedSpatialIndex.Designer.cs | 658 -------------- .../20260415151538_AddedSpatialIndex.cs | 25 - .../SqlServerDbContextModelSnapshot.cs | 655 -------------- .../Api.Data.SqlServer.Spatial/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 24 - Api.Data.SqlServer.Spatial/Dockerfile | 35 - Api.Data.SqlServer.Spatial/LICENSE | 18 - Api.Data.SqlServer.Spatial/README.md | 35 - Api.Data.SqlServer.Spatial/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Data.SqlServer/.docker/docker-compose.yml | 30 - Api.Data.SqlServer/.dockerignore | 12 - Api.Data.SqlServer/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 239 ----- Api.Data.SqlServer/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - Api.Data.SqlServer/.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 89 -- Api.Data.SqlServer/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.SqlServer.csproj | 23 - .../Api.Data.SqlServer.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 34 - .../Api.Data.SqlServer.Models/Example.cs | 14 - .../ExampleCreatable.cs | 14 - .../ExampleCreatableAndEditable.cs | 14 - .../ExampleDeletable.cs | 14 - .../ExampleUpdatable.cs | 14 - Api.Data.SqlServer/Api.Data.SqlServer.sln | 147 --- .../Api.Data.SqlServer.csproj | 37 - .../ExampleCreatableAndEditableController.cs | 15 - .../ExampleCreateablesController.cs | 15 - .../ExampleDeletablesController.cs | 15 - .../ExampleUpdatablesController.cs | 15 - .../Controllers/ExamplesController.cs | 15 - .../ExampleCreatableAndUpdatableMapping.cs | 26 - .../Data/Mappings/ExampleCreatableMapping.cs | 26 - .../Data/Mappings/ExampleDeletableMapping.cs | 26 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/Mappings/ExampleUpdatableMapping.cs | 26 - .../Data/SqlServerDbContext.cs | 10 - .../Data/SqlServerDbContextFactory.cs | 7 - .../Api.Data.SqlServer/Dockerfile.Local | 6 - .../20260415151054_Initial.Designer.cs | 773 ---------------- .../Migrations/20260415151054_Initial.cs | 672 -------------- .../SqlServerDbContextModelSnapshot.cs | 770 ---------------- .../Api.Data.SqlServer/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.SqlServer/appsettings.json | 42 - Api.Data.SqlServer/Dockerfile | 35 - Api.Data.SqlServer/LICENSE | 18 - Api.Data.SqlServer/README.md | 222 ----- Api.Data.SqlServer/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Data.Triggers/.docker/docker-compose.yml | 32 - Api.Data.Triggers/.dockerignore | 12 - Api.Data.Triggers/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 211 ----- Api.Data.Triggers/.gitignore | 11 - Api.Data.Triggers/.kubernetes/autoscaler.yaml | 25 - Api.Data.Triggers/.kubernetes/configmap.yaml | 8 - Api.Data.Triggers/.kubernetes/deployment.yaml | 89 -- Api.Data.Triggers/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Data.Triggers.csproj | 23 - .../Api.Data.Triggers.Models.csproj | 77 -- .../Criterias/ExampleQueryCriteria.cs | 17 - .../Criterias/ExampleTriggerQueryCriteria.cs | 17 - .../Api.Data.Triggers.Models/Example.cs | 22 - .../ExampleTrigger.cs | 22 - Api.Data.Triggers/Api.Data.Triggers.sln | 147 --- .../Api.Data.Triggers.csproj | 37 - .../Controllers/ExampleTriggersController.cs | 15 - .../Controllers/ExamplesController.cs | 15 - .../Data/Mappings/ExampleMapping.cs | 52 -- .../Data/Mappings/ExampleTriggerMapping.cs | 28 - .../Api.Data.Triggers/Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Data/Triggers/ExampleTriggers.cs | 72 -- .../Api.Data.Triggers/Dockerfile.Local | 6 - .../20260415151450_Initial.Designer.cs | 689 -------------- .../Migrations/20260415151450_Initial.cs | 638 ------------- .../Migrations/MySqlDbContextModelSnapshot.cs | 686 -------------- .../Api.Data.Triggers/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Data.Triggers/appsettings.json | 16 - Api.Data.Triggers/Dockerfile | 35 - Api.Data.Triggers/LICENSE | 18 - Api.Data.Triggers/README.md | 26 - Api.Data.Triggers/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 20 - Api.Documentation.Nonce/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 189 ---- Api.Documentation.Nonce/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/certificate.yaml | 19 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/ingress.yaml | 28 - .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Documentation.Nonce.csproj | 23 - .../Api.Documentation.Nonce.Models.csproj | 74 -- .../Api.Documentation.Nonce.sln | 136 --- .../Api.Documentation.Nonce.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.Documentation.Nonce/Dockerfile.Local | 6 - .../Api.Documentation.Nonce/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 19 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Documentation.Nonce/appsettings.json | 46 - Api.Documentation.Nonce/Dockerfile | 35 - Api.Documentation.Nonce/LICENSE | 18 - Api.Documentation.Nonce/README.md | 81 -- Api.Documentation.Nonce/icon.png | Bin 14103 -> 0 bytes Api.Documentation.Nonce/localhost.pfx | Bin 2670 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Documentation/.docker/docker-compose.yml | 17 - Api.Documentation/.dockerignore | 12 - Api.Documentation/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Documentation/.gitignore | 11 - Api.Documentation/.kubernetes/autoscaler.yaml | 25 - Api.Documentation/.kubernetes/configmap.yaml | 8 - Api.Documentation/.kubernetes/deployment.yaml | 83 -- Api.Documentation/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Documentation.csproj | 23 - .../Api.Documentation.Models.csproj | 75 -- Api.Documentation/Api.Documentation.sln | 133 --- .../Api.Documentation.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.Documentation/Dockerfile.Local | 6 - .../Api.Documentation/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Documentation/appsettings.json | 30 - Api.Documentation/Dockerfile | 35 - Api.Documentation/LICENSE | 18 - Api.Documentation/README.md | 59 -- Api.Documentation/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.ErrorHandling/.docker/docker-compose.yml | 17 - Api.ErrorHandling/.dockerignore | 12 - Api.ErrorHandling/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.ErrorHandling/.gitignore | 11 - Api.ErrorHandling/.kubernetes/autoscaler.yaml | 25 - Api.ErrorHandling/.kubernetes/configmap.yaml | 8 - Api.ErrorHandling/.kubernetes/deployment.yaml | 83 -- Api.ErrorHandling/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.ErrorHandling.csproj | 23 - .../Api.ErrorHandling.Models.csproj | 75 -- Api.ErrorHandling/Api.ErrorHandling.sln | 134 --- .../Api.ErrorHandling.csproj | 37 - .../Controllers/ExamplesController.cs | 235 ----- .../Api.ErrorHandling/Dockerfile.Local | 6 - .../Api.ErrorHandling/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.ErrorHandling/appsettings.json | 16 - Api.ErrorHandling/Dockerfile | 35 - Api.ErrorHandling/LICENSE | 18 - Api.ErrorHandling/README.md | 54 -- Api.ErrorHandling/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 34 - Api.Eventing.RabbitMq/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Eventing.RabbitMq/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 84 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Eventing.RabbitMq.csproj | 23 - .../Api.Eventing.RabbitMq.Models.csproj | 77 -- .../Api.Eventing.RabbitMq.sln | 147 --- .../Api.Eventing.RabbitMq.csproj | 37 - .../Controllers/ExamplesController.cs | 58 -- .../Api.Eventing.RabbitMq/Dockerfile.Local | 6 - .../Eventing/EventingHandler.cs | 19 - .../Eventing/EventingHandlerRoutingKey.cs | 24 - .../Eventing/Models/EventModel.cs | 12 - .../Eventing/Models/EventModelRoutingKey.cs | 12 - .../Api.Eventing.RabbitMq/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Eventing.RabbitMq/appsettings.json | 31 - Api.Eventing.RabbitMq/Dockerfile | 35 - Api.Eventing.RabbitMq/LICENSE | 18 - Api.Eventing.RabbitMq/README.md | 134 --- Api.Eventing.RabbitMq/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.HealthChecks/.docker/docker-compose.yml | 17 - Api.HealthChecks/.dockerignore | 12 - Api.HealthChecks/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 236 ----- Api.HealthChecks/.gitignore | 11 - Api.HealthChecks/.kubernetes/autoscaler.yaml | 25 - Api.HealthChecks/.kubernetes/configmap.yaml | 8 - Api.HealthChecks/.kubernetes/deployment.yaml | 83 -- Api.HealthChecks/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.HealthChecks.csproj | 23 - .../Api.HealthChecks.Models.csproj | 75 -- Api.HealthChecks/Api.HealthChecks.sln | 133 --- .../Api.HealthChecks/Api.HealthChecks.csproj | 37 - .../Controllers/ExamplesController.cs | 70 -- .../Api.HealthChecks/Dockerfile.Local | 6 - Api.HealthChecks/Api.HealthChecks/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Api.HealthChecks/appsettings.Staging.json | 2 - .../Api.HealthChecks/appsettings.json | 15 - Api.HealthChecks/Dockerfile | 35 - Api.HealthChecks/LICENSE | 18 - Api.HealthChecks/README.md | 125 --- Api.HealthChecks/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Hosting.Http/.docker/docker-compose.yml | 17 - Api.Hosting.Http/.dockerignore | 12 - Api.Hosting.Http/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Hosting.Http/.gitignore | 11 - Api.Hosting.Http/.kubernetes/autoscaler.yaml | 25 - Api.Hosting.Http/.kubernetes/configmap.yaml | 8 - Api.Hosting.Http/.kubernetes/deployment.yaml | 83 -- Api.Hosting.Http/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Hosting.Http.csproj | 23 - .../Api.Hosting.Http.Models.csproj | 75 -- Api.Hosting.Http/Api.Hosting.Http.sln | 133 --- .../Api.Hosting.Http/Api.Hosting.Http.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.Hosting.Http/Dockerfile.Local | 6 - Api.Hosting.Http/Api.Hosting.Http/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Api.Hosting.Http/appsettings.Staging.json | 2 - .../Api.Hosting.Http/appsettings.json | 13 - Api.Hosting.Http/Dockerfile | 35 - Api.Hosting.Http/LICENSE | 18 - Api.Hosting.Http/README.md | 30 - Api.Hosting.Http/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Hosting.Https/.docker/docker-compose.yml | 20 - Api.Hosting.Https/.dockerignore | 12 - Api.Hosting.Https/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 188 ---- Api.Hosting.Https/.gitignore | 11 - Api.Hosting.Https/.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/certificate.yaml | 19 - Api.Hosting.Https/.kubernetes/configmap.yaml | 8 - Api.Hosting.Https/.kubernetes/deployment.yaml | 83 -- Api.Hosting.Https/.kubernetes/ingress.yaml | 22 - Api.Hosting.Https/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Hosting.Https.csproj | 23 - .../Api.Hosting.Https.Models.csproj | 74 -- Api.Hosting.Https/Api.Hosting.Https.sln | 136 --- .../Api.Hosting.Https.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.Hosting.Https/Dockerfile.Local | 6 - .../Api.Hosting.Https/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 19 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Hosting.Https/appsettings.json | 13 - Api.Hosting.Https/Dockerfile | 35 - Api.Hosting.Https/LICENSE | 18 - Api.Hosting.Https/README.md | 105 --- Api.Hosting.Https/icon.png | Bin 14103 -> 0 bytes Api.Hosting.Https/localhost.pfx | Bin 2670 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.Hosting.MultipartLimits/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Hosting.MultipartLimits/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Hosting.MultipartLimits.csproj | 23 - .../Api.Hosting.MultipartLimits.Models.csproj | 75 -- .../Api.Hosting.MultipartLimits.sln | 140 --- .../Api.Hosting.MultipartLimits.csproj | 37 - .../Controllers/ExamplesController.cs | 37 - .../Dockerfile.Local | 6 - .../Api.Hosting.MultipartLimits/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 17 - Api.Hosting.MultipartLimits/Dockerfile | 35 - Api.Hosting.MultipartLimits/LICENSE | 18 - Api.Hosting.MultipartLimits/README.md | 47 - Api.Hosting.MultipartLimits/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Localization/.docker/docker-compose.yml | 17 - Api.Localization/.dockerignore | 12 - Api.Localization/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Localization/.gitignore | 11 - Api.Localization/.kubernetes/autoscaler.yaml | 25 - Api.Localization/.kubernetes/configmap.yaml | 8 - Api.Localization/.kubernetes/deployment.yaml | 83 -- Api.Localization/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Localization.csproj | 23 - .../Api.Localization.Models.csproj | 75 -- Api.Localization/Api.Localization.sln | 133 --- .../Api.Localization/Api.Localization.csproj | 37 - .../Controllers/ExamplesController.cs | 38 - .../Api.Localization/Dockerfile.Local | 6 - Api.Localization/Api.Localization/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Api.Localization/appsettings.Staging.json | 2 - .../Api.Localization/appsettings.json | 19 - Api.Localization/Dockerfile | 35 - Api.Localization/LICENSE | 18 - Api.Localization/README.md | 52 -- Api.Localization/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.Logging.Log4Net/.dockerignore | 12 - Api.Logging.Log4Net/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Logging.Log4Net/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- Api.Logging.Log4Net/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Logging.Log4Net.csproj | 23 - .../Api.Logging.Log4Net.Models.csproj | 77 -- Api.Logging.Log4Net/Api.Logging.Log4Net.sln | 147 --- .../Api.Logging.Log4Net.csproj | 37 - .../Controllers/ExamplesController.cs | 60 -- .../Api.Logging.Log4Net/Dockerfile.Local | 6 - .../Api.Logging.Log4Net/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Logging.Log4Net/appsettings.json | 22 - Api.Logging.Log4Net/Dockerfile | 35 - Api.Logging.Log4Net/LICENSE | 18 - Api.Logging.Log4Net/README.md | 62 -- Api.Logging.Log4Net/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.Logging.Microsoft/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Logging.Microsoft/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Logging.Microsoft.csproj | 23 - .../Api.Logging.Microsoft.Models.csproj | 77 -- .../Api.Logging.Microsoft.sln | 147 --- .../Api.Logging.Microsoft.csproj | 37 - .../Controllers/ExamplesController.cs | 60 -- .../Api.Logging.Microsoft/Dockerfile.Local | 6 - .../Api.Logging.Microsoft/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Logging.Microsoft/appsettings.json | 22 - Api.Logging.Microsoft/Dockerfile | 35 - Api.Logging.Microsoft/LICENSE | 18 - Api.Logging.Microsoft/README.md | 62 -- Api.Logging.Microsoft/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Logging.NLog/.docker/docker-compose.yml | 17 - Api.Logging.NLog/.dockerignore | 12 - Api.Logging.NLog/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Logging.NLog/.gitignore | 11 - Api.Logging.NLog/.kubernetes/autoscaler.yaml | 25 - Api.Logging.NLog/.kubernetes/configmap.yaml | 8 - Api.Logging.NLog/.kubernetes/deployment.yaml | 83 -- Api.Logging.NLog/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Logging.NLog.csproj | 23 - .../Api.Logging.NLog.Models.csproj | 77 -- Api.Logging.NLog/Api.Logging.NLog.sln | 147 --- .../Api.Logging.NLog/Api.Logging.NLog.csproj | 37 - .../Controllers/ExamplesController.cs | 60 -- .../Api.Logging.NLog/Dockerfile.Local | 6 - Api.Logging.NLog/Api.Logging.NLog/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Api.Logging.NLog/appsettings.Staging.json | 2 - .../Api.Logging.NLog/appsettings.json | 22 - Api.Logging.NLog/Dockerfile | 35 - Api.Logging.NLog/LICENSE | 18 - Api.Logging.NLog/README.md | 62 -- Api.Logging.NLog/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.Logging.Serilog/.dockerignore | 12 - Api.Logging.Serilog/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Logging.Serilog/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- Api.Logging.Serilog/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Logging.Serilog.csproj | 23 - .../Api.Logging.Serilog.Models.csproj | 77 -- Api.Logging.Serilog/Api.Logging.Serilog.sln | 147 --- .../Api.Logging.Serilog.csproj | 37 - .../Controllers/ExamplesController.cs | 60 -- .../Api.Logging.Serilog/Dockerfile.Local | 6 - .../Api.Logging.Serilog/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Logging.Serilog/appsettings.json | 22 - Api.Logging.Serilog/Dockerfile | 35 - Api.Logging.Serilog/LICENSE | 18 - Api.Logging.Serilog/README.md | 62 -- Api.Logging.Serilog/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.MultipartJson/.docker/docker-compose.yml | 17 - Api.MultipartJson/.dockerignore | 12 - Api.MultipartJson/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.MultipartJson/.gitignore | 11 - Api.MultipartJson/.kubernetes/autoscaler.yaml | 25 - Api.MultipartJson/.kubernetes/configmap.yaml | 8 - Api.MultipartJson/.kubernetes/deployment.yaml | 83 -- Api.MultipartJson/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.MultipartJson.csproj | 23 - .../Api.MultipartJson.Models.csproj | 75 -- Api.MultipartJson/Api.MultipartJson.sln | 133 --- .../Api.MultipartJson.csproj | 37 - .../Controllers/ExamplesController.cs | 42 - .../Controllers/Requests/JsonBody.cs | 15 - .../Api.MultipartJson/Dockerfile.Local | 6 - .../Api.MultipartJson/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.MultipartJson/appsettings.json | 13 - Api.MultipartJson/Dockerfile | 35 - Api.MultipartJson/LICENSE | 18 - Api.MultipartJson/README.md | 30 - Api.MultipartJson/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 20 - .../.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 188 ---- .../.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/certificate.yaml | 19 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/ingress.yaml | 22 - .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ...PolicyHeaders.ContentSecurityPolicy.csproj | 23 - ...eaders.ContentSecurityPolicy.Models.csproj | 75 -- ...pi.PolicyHeaders.ContentSecurityPolicy.sln | 137 --- ...PolicyHeaders.ContentSecurityPolicy.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Dockerfile.Local | 6 - .../Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 19 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 76 -- .../wwwroot/csp-violation.html | 17 - .../Dockerfile | 35 - .../LICENSE | 18 - .../README.md | 130 --- .../icon.png | Bin 14103 -> 0 bytes .../localhost.pfx | Bin 2670 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - .../.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- .../.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ...pi.PolicyHeaders.ContentTypeOptions.csproj | 23 - ...cyHeaders.ContentTypeOptions.Models.csproj | 75 -- .../Api.PolicyHeaders.ContentTypeOptions.sln | 134 --- ...pi.PolicyHeaders.ContentTypeOptions.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Dockerfile.Local | 6 - .../Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 18 - ...ontent-type-sniff-violation-wrong-type.txt | 1 - .../wwwroot/content-type-sniff-violation.html | 18 - .../Dockerfile | 35 - Api.PolicyHeaders.ContentTypeOptions/LICENSE | 18 - .../README.md | 41 - Api.PolicyHeaders.ContentTypeOptions/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.PolicyHeaders.Cors/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.PolicyHeaders.Cors/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.PolicyHeaders.Cors.csproj | 23 - .../Api.PolicyHeaders.Cors.Models.csproj | 75 -- .../Api.PolicyHeaders.Cors.sln | 134 --- .../Api.PolicyHeaders.Cors.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.PolicyHeaders.Cors/Dockerfile.Local | 6 - .../Api.PolicyHeaders.Cors/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.PolicyHeaders.Cors/appsettings.json | 36 - ...s-violation-credetntials-not-alloweed.html | 19 - .../cors-violation-header-not-allowed.html | 22 - .../cors-violation-method-not-allowed.html | 19 - .../cors-violation-origin-not-allowed.html | 17 - .../cors-violation-preflight-blocked.html | 23 - Api.PolicyHeaders.Cors/Dockerfile | 35 - Api.PolicyHeaders.Cors/LICENSE | 18 - Api.PolicyHeaders.Cors/README.md | 54 -- Api.PolicyHeaders.Cors/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - .../.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.PolicyHeaders.ForwardedHeaders/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ....Api.PolicyHeaders.ForwardedHeaders.csproj | 23 - ...licyHeaders.ForwardedHeaders.Models.csproj | 75 -- .../Api.PolicyHeaders.ForwardedHeaders.sln | 134 --- .../Api.PolicyHeaders.ForwardedHeaders.csproj | 37 - .../Controllers/ExamplesController.cs | 40 - .../Dockerfile.Local | 6 - .../Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 19 - Api.PolicyHeaders.ForwardedHeaders/Dockerfile | 35 - Api.PolicyHeaders.ForwardedHeaders/LICENSE | 18 - Api.PolicyHeaders.ForwardedHeaders/README.md | 42 - Api.PolicyHeaders.ForwardedHeaders/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.PolicyHeaders.FrameOptions/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.PolicyHeaders.FrameOptions/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ...ests.Api.PolicyHeaders.FrameOptions.csproj | 23 - ...i.PolicyHeaders.FrameOptions.Models.csproj | 75 -- .../Api.PolicyHeaders.FrameOptions.sln | 134 --- .../Api.PolicyHeaders.FrameOptions.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Dockerfile.Local | 6 - .../Api.PolicyHeaders.FrameOptions/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 18 - .../wwwroot/frame-options-violation.html | 27 - Api.PolicyHeaders.FrameOptions/Dockerfile | 35 - Api.PolicyHeaders.FrameOptions/LICENSE | 18 - Api.PolicyHeaders.FrameOptions/README.md | 40 - Api.PolicyHeaders.FrameOptions/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 20 - Api.PolicyHeaders.Hsts/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 188 ---- Api.PolicyHeaders.Hsts/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/certificate.yaml | 19 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/ingress.yaml | 22 - .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.PolicyHeaders.Hsts.csproj | 23 - .../Api.PolicyHeaders.Hsts.Models.csproj | 75 -- .../Api.PolicyHeaders.Hsts.sln | 137 --- .../Api.PolicyHeaders.Hsts.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.PolicyHeaders.Hsts/Dockerfile.Local | 6 - .../Api.PolicyHeaders.Hsts/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 15 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.PolicyHeaders.Hsts/appsettings.json | 20 - .../wwwroot/hsts-violation-image.png | Bin 15262 -> 0 bytes .../wwwroot/hsts-violation.html | 41 - Api.PolicyHeaders.Hsts/Dockerfile | 35 - Api.PolicyHeaders.Hsts/LICENSE | 18 - Api.PolicyHeaders.Hsts/README.md | 43 - Api.PolicyHeaders.Hsts/icon.png | Bin 14103 -> 0 bytes Api.PolicyHeaders.Hsts/localhost.pfx | Bin 2670 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - .../.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.PolicyHeaders.ReferrerPolicy/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ...ts.Api.PolicyHeaders.ReferrerPolicy.csproj | 23 - ...PolicyHeaders.ReferrerPolicy.Models.csproj | 75 -- .../Api.PolicyHeaders.ReferrerPolicy.sln | 134 --- .../Api.PolicyHeaders.ReferrerPolicy.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Dockerfile.Local | 6 - .../Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 18 - .../wwwroot/referrer-policy-violation.html | 55 -- Api.PolicyHeaders.ReferrerPolicy/Dockerfile | 35 - Api.PolicyHeaders.ReferrerPolicy/LICENSE | 18 - Api.PolicyHeaders.ReferrerPolicy/README.md | 41 - Api.PolicyHeaders.ReferrerPolicy/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.PolicyHeaders.Robots/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.PolicyHeaders.Robots/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.PolicyHeaders.Robots.csproj | 23 - .../Api.PolicyHeaders.Robots.Models.csproj | 75 -- .../Api.PolicyHeaders.Robots.sln | 134 --- .../Api.PolicyHeaders.Robots.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.PolicyHeaders.Robots/Dockerfile.Local | 6 - .../Api.PolicyHeaders.Robots/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.PolicyHeaders.Robots/appsettings.json | 24 - Api.PolicyHeaders.Robots/Dockerfile | 35 - Api.PolicyHeaders.Robots/LICENSE | 18 - Api.PolicyHeaders.Robots/README.md | 48 - Api.PolicyHeaders.Robots/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.PolicyHeaders.XssProtection/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.PolicyHeaders.XssProtection/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - ...sts.Api.PolicyHeaders.XssProtection.csproj | 23 - ....PolicyHeaders.XssProtection.Models.csproj | 75 -- .../Api.PolicyHeaders.XssProtection.sln | 134 --- .../Api.PolicyHeaders.XssProtection.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Dockerfile.Local | 6 - .../Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 19 - .../wwwroot/xss-violation.html | 22 - Api.PolicyHeaders.XssProtection/Dockerfile | 35 - Api.PolicyHeaders.XssProtection/LICENSE | 18 - Api.PolicyHeaders.XssProtection/README.md | 49 - Api.PolicyHeaders.XssProtection/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.RequestTracing/.docker/docker-compose.yml | 17 - Api.RequestTracing/.dockerignore | 12 - Api.RequestTracing/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.RequestTracing/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - Api.RequestTracing/.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- Api.RequestTracing/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.RequestTracing.csproj | 23 - .../Api.RequestTracing.Models.csproj | 75 -- Api.RequestTracing/Api.RequestTracing.sln | 133 --- .../Api.RequestTracing.csproj | 37 - .../Controllers/ExamplesController.cs | 34 - .../Api.RequestTracing/Dockerfile.Local | 6 - .../Api.RequestTracing/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.RequestTracing/appsettings.json | 13 - Api.RequestTracing/Dockerfile | 35 - Api.RequestTracing/LICENSE | 18 - Api.RequestTracing/README.md | 31 - Api.RequestTracing/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.ResponseCache/.docker/docker-compose.yml | 17 - Api.ResponseCache/.dockerignore | 12 - Api.ResponseCache/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.ResponseCache/.gitignore | 11 - Api.ResponseCache/.kubernetes/autoscaler.yaml | 25 - Api.ResponseCache/.kubernetes/configmap.yaml | 8 - Api.ResponseCache/.kubernetes/deployment.yaml | 83 -- Api.ResponseCache/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.ResponseCache.csproj | 23 - .../Api.ResponseCache.Models.csproj | 75 -- Api.ResponseCache/Api.ResponseCache.sln | 133 --- .../Api.ResponseCache.csproj | 37 - .../Controllers/ExamplesController.cs | 48 - .../Api.ResponseCache/Dockerfile.Local | 6 - .../Api.ResponseCache/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.ResponseCache/appsettings.json | 18 - Api.ResponseCache/Dockerfile | 35 - Api.ResponseCache/LICENSE | 18 - Api.ResponseCache/README.md | 43 - Api.ResponseCache/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - .../.docker/docker-compose.yml | 17 - Api.ResponseCompression/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.ResponseCompression/.gitignore | 11 - .../.kubernetes/autoscaler.yaml | 25 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/deployment.yaml | 83 -- .../.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.ResponseCompression.csproj | 23 - .../Api.ResponseCompression.Models.csproj | 75 -- .../Api.ResponseCompression.sln | 133 --- .../Api.ResponseCompression.csproj | 37 - .../Controllers/ExamplesController.cs | 36 - .../Api.ResponseCompression/Dockerfile.Local | 6 - .../Api.ResponseCompression/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.ResponseCompression/appsettings.json | 17 - Api.ResponseCompression/Dockerfile | 35 - Api.ResponseCompression/LICENSE | 18 - Api.ResponseCompression/README.md | 42 - Api.ResponseCompression/icon.png | Bin 14103 -> 0 bytes Api.Session/.docker/docker-compose.dcproj | 13 - Api.Session/.docker/docker-compose.yml | 17 - Api.Session/.dockerignore | 12 - Api.Session/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Session/.gitignore | 11 - Api.Session/.kubernetes/autoscaler.yaml | 25 - Api.Session/.kubernetes/configmap.yaml | 8 - Api.Session/.kubernetes/deployment.yaml | 83 -- Api.Session/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Session.csproj | 23 - .../Api.Session.Models.csproj | 75 -- Api.Session/Api.Session.sln | 133 --- Api.Session/Api.Session/Api.Session.csproj | 37 - .../Controllers/ExamplesController.cs | 82 -- Api.Session/Api.Session/Dockerfile.Local | 6 - Api.Session/Api.Session/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Api.Session/appsettings.Development.json | 2 - .../Api.Session/appsettings.Production.json | 2 - .../Api.Session/appsettings.Staging.json | 2 - Api.Session/Api.Session/appsettings.json | 16 - Api.Session/Dockerfile | 35 - Api.Session/LICENSE | 18 - Api.Session/README.md | 42 - Api.Session/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.StartupTasks/.docker/docker-compose.yml | 17 - Api.StartupTasks/.dockerignore | 12 - Api.StartupTasks/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.StartupTasks/.gitignore | 11 - Api.StartupTasks/.kubernetes/autoscaler.yaml | 25 - Api.StartupTasks/.kubernetes/configmap.yaml | 8 - Api.StartupTasks/.kubernetes/deployment.yaml | 83 -- Api.StartupTasks/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.StartupTasks.csproj | 23 - .../Api.StartupTasks.Models.csproj | 75 -- Api.StartupTasks/Api.StartupTasks.sln | 133 --- .../Api.StartupTasks/Api.StartupTasks.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.StartupTasks/Dockerfile.Local | 6 - Api.StartupTasks/Api.StartupTasks/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Startup/ExampleStartupTask.cs | 30 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Api.StartupTasks/appsettings.Staging.json | 2 - .../Api.StartupTasks/appsettings.json | 15 - Api.StartupTasks/Dockerfile | 35 - Api.StartupTasks/LICENSE | 18 - Api.StartupTasks/README.md | 42 - Api.StartupTasks/icon.png | Bin 14103 -> 0 bytes Api.StaticFiles/.docker/docker-compose.dcproj | 13 - Api.StaticFiles/.docker/docker-compose.yml | 17 - Api.StaticFiles/.dockerignore | 12 - Api.StaticFiles/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.StaticFiles/.gitignore | 11 - Api.StaticFiles/.kubernetes/autoscaler.yaml | 25 - Api.StaticFiles/.kubernetes/configmap.yaml | 8 - Api.StaticFiles/.kubernetes/deployment.yaml | 83 -- Api.StaticFiles/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.StaticFiles.csproj | 23 - .../Api.StaticFiles.Models.csproj | 75 -- Api.StaticFiles/Api.StaticFiles.sln | 133 --- .../Api.StaticFiles/Api.StaticFiles.csproj | 37 - .../Controllers/ExamplesController.cs | 31 - .../Api.StaticFiles/Dockerfile.Local | 6 - Api.StaticFiles/Api.StaticFiles/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Api.StaticFiles/appsettings.Staging.json | 2 - .../Api.StaticFiles/appsettings.json | 13 - .../fonts/open-sans-v44-latin-regular.woff2 | Bin 18640 -> 0 bytes .../Api.StaticFiles/wwwroot/images/image.jpg | Bin 256546 -> 0 bytes .../Api.StaticFiles/wwwroot/index.html | 22 - Api.StaticFiles/Dockerfile | 35 - Api.StaticFiles/LICENSE | 18 - Api.StaticFiles/README.md | 31 - Api.StaticFiles/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Storage.Azure/.docker/docker-compose.yml | 19 - Api.Storage.Azure/.dockerignore | 12 - Api.Storage.Azure/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 188 ---- Api.Storage.Azure/.gitignore | 11 - Api.Storage.Azure/.kubernetes/autoscaler.yaml | 25 - Api.Storage.Azure/.kubernetes/configmap.yaml | 8 - Api.Storage.Azure/.kubernetes/deployment.yaml | 107 --- Api.Storage.Azure/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Storage.Azure.csproj | 23 - .../Api.Storage.Azure.Models.csproj | 77 -- Api.Storage.Azure/Api.Storage.Azure.sln | 147 --- .../Api.Storage.Azure.csproj | 38 - .../Controllers/ExamplesController.cs | 44 - .../Api.Storage.Azure/Dockerfile.Local | 6 - .../Api.Storage.Azure/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Storage.Azure/appsettings.json | 25 - Api.Storage.Azure/Dockerfile | 35 - Api.Storage.Azure/LICENSE | 18 - Api.Storage.Azure/README.md | 155 ---- Api.Storage.Azure/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 13 - Api.Storage.Local/.docker/docker-compose.yml | 19 - Api.Storage.Local/.dockerignore | 12 - Api.Storage.Local/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 185 ---- Api.Storage.Local/.gitignore | 11 - Api.Storage.Local/.kubernetes/autoscaler.yaml | 25 - Api.Storage.Local/.kubernetes/configmap.yaml | 8 - Api.Storage.Local/.kubernetes/deployment.yaml | 89 -- Api.Storage.Local/.kubernetes/service.yaml | 12 - .../.kubernetes/storage-pvc.yaml | 11 - .../.kubernetes/storage-storageclass.yaml | 10 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Storage.Local.csproj | 23 - .../Api.Storage.Local.Models.csproj | 77 -- Api.Storage.Local/Api.Storage.Local.sln | 149 --- .../Api.Storage.Local.csproj | 38 - .../Controllers/ExamplesController.cs | 44 - .../Api.Storage.Local/Dockerfile.Local | 6 - .../Api.Storage.Local/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Api.Storage.Local/appsettings.json | 21 - Api.Storage.Local/Dockerfile | 35 - Api.Storage.Local/LICENSE | 18 - Api.Storage.Local/README.md | 126 --- Api.Storage.Local/icon.png | Bin 14103 -> 0 bytes Api.TimeZone/.docker/docker-compose.dcproj | 13 - Api.TimeZone/.docker/docker-compose.yml | 17 - Api.TimeZone/.dockerignore | 12 - Api.TimeZone/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.TimeZone/.gitignore | 11 - Api.TimeZone/.kubernetes/autoscaler.yaml | 25 - Api.TimeZone/.kubernetes/configmap.yaml | 8 - Api.TimeZone/.kubernetes/deployment.yaml | 83 -- Api.TimeZone/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.TimeZone.csproj | 23 - .../Api.TimeZone.Models.csproj | 75 -- Api.TimeZone/Api.TimeZone.sln | 133 --- Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj | 37 - .../Controllers/ExamplesController.cs | 71 -- .../Controllers/Requests/DateTimeRequest.cs | 16 - Api.TimeZone/Api.TimeZone/Dockerfile.Local | 6 - Api.TimeZone/Api.TimeZone/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Api.TimeZone/appsettings.Development.json | 2 - .../Api.TimeZone/appsettings.Production.json | 2 - .../Api.TimeZone/appsettings.Staging.json | 2 - Api.TimeZone/Api.TimeZone/appsettings.json | 16 - Api.TimeZone/Dockerfile | 35 - Api.TimeZone/LICENSE | 18 - Api.TimeZone/README.md | 52 -- Api.TimeZone/icon.png | Bin 14103 -> 0 bytes Api.Versioning/.docker/docker-compose.dcproj | 13 - Api.Versioning/.docker/docker-compose.yml | 17 - Api.Versioning/.dockerignore | 12 - Api.Versioning/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.Versioning/.gitignore | 11 - Api.Versioning/.kubernetes/autoscaler.yaml | 25 - Api.Versioning/.kubernetes/configmap.yaml | 8 - Api.Versioning/.kubernetes/deployment.yaml | 83 -- Api.Versioning/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Versioning.csproj | 23 - .../Api.Versioning.Models.csproj | 75 -- Api.Versioning/Api.Versioning.sln | 133 --- .../Api.Versioning/Api.Versioning.csproj | 37 - .../Controllers/ExamplesController.cs | 51 -- .../Api.Versioning/Dockerfile.Local | 6 - Api.Versioning/Api.Versioning/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Api.Versioning/appsettings.Staging.json | 2 - .../Api.Versioning/appsettings.json | 13 - Api.Versioning/Dockerfile | 35 - Api.Versioning/LICENSE | 18 - Api.Versioning/README.md | 35 - Api.Versioning/icon.png | Bin 14103 -> 0 bytes Api.VirusScan/.docker/docker-compose.dcproj | 13 - Api.VirusScan/.docker/docker-compose.yml | 27 - Api.VirusScan/.dockerignore | 12 - Api.VirusScan/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api.VirusScan/.gitignore | 11 - Api.VirusScan/.kubernetes/autoscaler.yaml | 25 - Api.VirusScan/.kubernetes/configmap.yaml | 8 - Api.VirusScan/.kubernetes/deployment.yaml | 83 -- Api.VirusScan/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.VirusScan.csproj | 23 - .../Api.VirusScan.Models.csproj | 75 -- Api.VirusScan/Api.VirusScan.sln | 140 --- .../Api.VirusScan/Api.VirusScan.csproj | 37 - .../Controllers/ExamplesController.cs | 36 - Api.VirusScan/Api.VirusScan/Dockerfile.Local | 6 - Api.VirusScan/Api.VirusScan/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../Api.VirusScan/appsettings.Production.json | 2 - .../Api.VirusScan/appsettings.Staging.json | 2 - Api.VirusScan/Api.VirusScan/appsettings.json | 22 - Api.VirusScan/Dockerfile | 35 - Api.VirusScan/LICENSE | 18 - Api.VirusScan/README.md | 71 -- Api.VirusScan/icon.png | Bin 14103 -> 0 bytes Api._Blank/.docker/docker-compose.dcproj | 13 - Api._Blank/.docker/docker-compose.yml | 17 - Api._Blank/.dockerignore | 12 - Api._Blank/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Api._Blank/.gitignore | 11 - Api._Blank/.kubernetes/autoscaler.yaml | 25 - Api._Blank/.kubernetes/configmap.yaml | 8 - Api._Blank/.kubernetes/deployment.yaml | 83 -- Api._Blank/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Api.Blank/Tests.Api.Blank.csproj | 23 - .../Api.Blank.Models/Api.Blank.Models.csproj | 75 -- Api._Blank/Api.Blank.sln | 133 --- Api._Blank/Api.Blank/Api.Blank.csproj | 37 - Api._Blank/Api.Blank/Dockerfile.Local | 6 - Api._Blank/Api.Blank/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Api.Blank/appsettings.Development.json | 2 - .../Api.Blank/appsettings.Production.json | 2 - Api._Blank/Api.Blank/appsettings.Staging.json | 2 - Api._Blank/Api.Blank/appsettings.json | 13 - Api._Blank/Dockerfile | 35 - Api._Blank/LICENSE | 18 - Api._Blank/README.md | 26 - Api._Blank/icon.png | Bin 14103 -> 0 bytes .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.CustomConfigSection/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.CustomConfigSection/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.CustomConfigSection.csproj | 23 - .../Console.CustomConfigSection.sln | 123 --- .../Config/CustomOptions.cs | 17 - .../Console.CustomConfigSection.csproj | 43 - .../Dockerfile.Local | 5 - .../Console.CustomConfigSection/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 35 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 8 - Console.CustomConfigSection/Dockerfile | 31 - Console.CustomConfigSection/README.md | 59 -- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.CustomService/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.CustomService/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.CustomService.csproj | 23 - .../Console.CustomService.sln | 123 --- .../Console.CustomService.csproj | 43 - .../Console.CustomService/Dockerfile.Local | 5 - .../Console.CustomService/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../Services/Abstractions/IExampleService.cs | 15 - .../Services/ExampleService.cs | 16 - .../Workers/ExampleWorker.cs | 35 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.CustomService/appsettings.json | 5 - Console.CustomService/Dockerfile | 31 - Console.CustomService/README.md | 38 - .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.Data.InMemory/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.Data.InMemory/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Data.InMemory.csproj | 23 - .../Console.Data.InMemory.sln | 137 --- .../Console.Data.InMemory.csproj | 45 - .../Data/InMemoryDbContext.cs | 10 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/Models/Example.cs | 14 - .../Console.Data.InMemory/Dockerfile.Local | 5 - .../Console.Data.InMemory/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 45 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Data.InMemory/appsettings.json | 27 - Console.Data.InMemory/Dockerfile | 31 - Console.Data.InMemory/README.md | 71 -- .../.docker/docker-compose.dcproj | 15 - Console.Data.MySql/.docker/docker-compose.yml | 29 - Console.Data.MySql/.dockerignore | 12 - Console.Data.MySql/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 182 ---- Console.Data.MySql/.gitignore | 11 - Console.Data.MySql/.kubernetes/configmap.yaml | 8 - Console.Data.MySql/.kubernetes/cronjob.yaml | 56 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Data.MySql.csproj | 23 - Console.Data.MySql/Console.Data.MySql.sln | 137 --- .../Console.Data.MySql.csproj | 45 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Console.Data.MySql/Data/Models/Example.cs | 14 - .../Console.Data.MySql/Data/MySqlDbContext.cs | 10 - .../Data/MySqlDbContextFactory.cs | 7 - .../Console.Data.MySql/Dockerfile.Local | 5 - .../20260311110547_Initial.Designer.cs | 572 ------------ .../Migrations/20260311110547_Initial.cs | 531 ----------- .../Migrations/MySqlDbContextModelSnapshot.cs | 569 ------------ .../Console.Data.MySql/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 45 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Data.MySql/appsettings.json | 27 - Console.Data.MySql/Dockerfile | 31 - Console.Data.MySql/README.md | 183 ---- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 27 - Console.Data.PostgreSQL/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 200 ----- Console.Data.PostgreSQL/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 56 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Data.PostgreSQL.csproj | 23 - .../Console.Data.PostgreSQL.sln | 137 --- .../Console.Data.PostgreSQL.csproj | 45 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/Models/Example.cs | 14 - .../Data/PostgreSqlDbContext.cs | 10 - .../Data/PostgreSqlDbContextFactory.cs | 7 - .../Console.Data.PostgreSQL/Dockerfile.Local | 5 - .../20260311120716_Initial.Designer.cs | 571 ------------ .../Migrations/20260311120716_Initial.cs | 477 ---------- .../PostgreSqlDbContextModelSnapshot.cs | 568 ------------ .../Console.Data.PostgreSQL/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 45 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Data.PostgreSQL/appsettings.json | 27 - Console.Data.PostgreSQL/Dockerfile | 31 - Console.Data.PostgreSQL/README.md | 196 ---- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 16 - Console.Data.SqLite/.dockerignore | 12 - Console.Data.SqLite/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 173 ---- Console.Data.SqLite/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - Console.Data.SqLite/.kubernetes/cronjob.yaml | 57 -- Console.Data.SqLite/.kubernetes/data-pvc.yaml | 11 - .../.kubernetes/data-storageclass.yaml | 10 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Data.SqLite.csproj | 23 - Console.Data.SqLite/Console.Data.SqLite.sln | 139 --- .../Console.Data.SqLite.csproj | 45 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/Models/Example.cs | 14 - .../Data/SqLiteDbContext.cs | 10 - .../Data/SqLiteDbContextFactory.cs | 7 - .../Console.Data.SqLite/Dockerfile.Local | 9 - .../20260311131050_Initial.Designer.cs | 559 ------------ .../Migrations/20260311131050_Initial.cs | 473 ---------- .../SqLiteDbContextModelSnapshot.cs | 556 ------------ .../Console.Data.SqLite/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 45 - .../appsettings.Development.json | 5 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Data.SqLite/appsettings.json | 27 - Console.Data.SqLite/Dockerfile | 35 - Console.Data.SqLite/README.md | 162 ---- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 27 - Console.Data.SqlServer/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 212 ----- Console.Data.SqlServer/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 56 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Data.SqlServer.csproj | 23 - .../Console.Data.SqlServer.sln | 137 --- .../Console.Data.SqlServer.csproj | 45 - .../Data/Mappings/ExampleMapping.cs | 26 - .../Data/Models/Example.cs | 14 - .../Data/SqlServerDbContext.cs | 10 - .../Data/SqlServerDbContextFactory.cs | 7 - .../Console.Data.SqlServer/Dockerfile.Local | 5 - .../20260311103638_Initial.Designer.cs | 574 ------------ .../Migrations/20260311103638_Initial.cs | 477 ---------- .../SqlServerDbContextModelSnapshot.cs | 571 ------------ .../Console.Data.SqlServer/Program.cs | 13 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 45 - .../appsettings.Development.json | 6 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Data.SqlServer/appsettings.json | 27 - Console.Data.SqlServer/Dockerfile | 31 - Console.Data.SqlServer/README.md | 208 ----- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 31 - Console.Eventing.RabbitMq/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.Eventing.RabbitMq/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 56 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Eventing.RabbitMq.csproj | 23 - .../Console.Eventing.RabbitMq.sln | 137 --- .../Console.Eventing.RabbitMq.csproj | 49 - .../Dockerfile.Local | 5 - .../Eventing/EventingHandler.cs | 24 - .../Eventing/Models/EventModel.cs | 12 - .../Console.Eventing.RabbitMq/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 36 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 18 - Console.Eventing.RabbitMq/Dockerfile | 31 - Console.Eventing.RabbitMq/README.md | 110 --- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.ExceptionHandling/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.ExceptionHandling/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.ExceptionHandling.csproj | 23 - .../Console.ExceptionHandling.sln | 123 --- .../Console.ExceptionHandling.csproj | 43 - .../Dockerfile.Local | 5 - .../Console.ExceptionHandling/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 28 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 5 - Console.ExceptionHandling/Dockerfile | 31 - Console.ExceptionHandling/README.md | 23 - .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.Localization/.dockerignore | 12 - Console.Localization/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.Localization/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - Console.Localization/.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Localization.csproj | 23 - Console.Localization/Console.Localization.sln | 123 --- .../Console.Localization.csproj | 43 - .../Console.Localization/Dockerfile.Local | 5 - .../Console.Localization/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 36 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Localization/appsettings.json | 8 - Console.Localization/Dockerfile | 31 - Console.Localization/README.md | 32 - .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.Logging.Log4Net/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.Logging.Log4Net/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Logging.Log4Net.csproj | 23 - .../Console.Logging.Log4Net.sln | 137 --- .../Console.Logging.Log4Net.csproj | 45 - .../Console.Logging.Log4Net/Dockerfile.Local | 5 - .../Console.Logging.Log4Net/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 30 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Logging.Log4Net/appsettings.json | 14 - Console.Logging.Log4Net/Dockerfile | 31 - Console.Logging.Log4Net/README.md | 54 -- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.Logging.Microsoft/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.Logging.Microsoft/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Logging.Microsoft.csproj | 23 - .../Console.Logging.Microsoft.sln | 137 --- .../Console.Logging.Microsoft.csproj | 45 - .../Dockerfile.Local | 5 - .../Console.Logging.Microsoft/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 30 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../appsettings.json | 14 - Console.Logging.Microsoft/Dockerfile | 31 - Console.Logging.Microsoft/README.md | 54 -- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.Logging.NLog/.dockerignore | 12 - Console.Logging.NLog/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.Logging.NLog/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - Console.Logging.NLog/.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Logging.NLog.csproj | 23 - Console.Logging.NLog/Console.Logging.NLog.sln | 137 --- .../Console.Logging.NLog.csproj | 45 - .../Console.Logging.NLog/Dockerfile.Local | 5 - .../Console.Logging.NLog/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 30 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Logging.NLog/appsettings.json | 14 - Console.Logging.NLog/Dockerfile | 31 - Console.Logging.NLog/README.md | 54 -- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.Logging.Serilog/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.Logging.Serilog/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Logging.Serilog.csproj | 23 - .../Console.Logging.Serilog.sln | 137 --- .../Console.Logging.Serilog.csproj | 45 - .../Console.Logging.Serilog/Dockerfile.Local | 5 - .../Console.Logging.Serilog/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 30 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Logging.Serilog/appsettings.json | 14 - Console.Logging.Serilog/Dockerfile | 31 - Console.Logging.Serilog/README.md | 54 -- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 14 - Console.StartupTasks/.dockerignore | 12 - Console.StartupTasks/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.StartupTasks/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - Console.StartupTasks/.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.StartupTasks.csproj | 23 - Console.StartupTasks/Console.StartupTasks.sln | 123 --- .../Console.StartupTasks.csproj | 43 - .../Console.StartupTasks/Dockerfile.Local | 5 - .../Console.StartupTasks/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Startup/ExampleStartupTask.cs | 30 - .../Workers/ExampleWorker.cs | 30 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.StartupTasks/appsettings.json | 5 - Console.StartupTasks/Dockerfile | 31 - Console.StartupTasks/README.md | 24 - .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 16 - Console.Storage.Azure/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 160 ---- Console.Storage.Azure/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 76 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Storage.Azure.csproj | 23 - .../Console.Storage.Azure.sln | 137 --- .../Console.Storage.Azure.csproj | 45 - .../Console.Storage.Azure/Dockerfile.Local | 5 - .../Console.Storage.Azure/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 34 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Storage.Azure/appsettings.json | 12 - Console.Storage.Azure/Dockerfile | 31 - Console.Storage.Azure/README.md | 124 --- .../.docker/docker-compose.dcproj | 15 - .../.docker/docker-compose.yml | 16 - Console.Storage.Local/.dockerignore | 12 - .../.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 158 ---- Console.Storage.Local/.gitignore | 11 - .../.kubernetes/configmap.yaml | 8 - .../.kubernetes/cronjob.yaml | 62 -- .../.kubernetes/storage-pvc.yaml | 11 - .../.kubernetes/storage-storageclass.yaml | 10 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Storage.Local.csproj | 23 - .../Console.Storage.Local.sln | 140 --- .../Console.Storage.Local.csproj | 45 - .../Console.Storage.Local/Dockerfile.Local | 5 - .../Console.Storage.Local/Program.cs | 12 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/ExampleWorker.cs | 34 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../appsettings.Staging.json | 2 - .../Console.Storage.Local/appsettings.json | 8 - Console.Storage.Local/Dockerfile | 31 - Console.Storage.Local/README.md | 99 -- Console.Workers/.docker/docker-compose.dcproj | 15 - Console.Workers/.docker/docker-compose.yml | 14 - Console.Workers/.dockerignore | 12 - Console.Workers/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console.Workers/.gitignore | 11 - Console.Workers/.kubernetes/configmap.yaml | 8 - Console.Workers/.kubernetes/cronjob.yaml | 52 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Workers.csproj | 23 - Console.Workers/Console.Workers.sln | 123 --- .../Console.Workers/Console.Workers.csproj | 43 - .../Console.Workers/Dockerfile.Local | 5 - Console.Workers/Console.Workers/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../Workers/AnotherExampleWorker.cs | 27 - .../Console.Workers/Workers/ExampleWorker.cs | 27 - .../appsettings.Development.json | 2 - .../appsettings.Production.json | 2 - .../Console.Workers/appsettings.Staging.json | 2 - .../Console.Workers/appsettings.json | 5 - Console.Workers/Dockerfile | 31 - Console.Workers/README.md | 23 - Console._Blank/.docker/docker-compose.dcproj | 15 - Console._Blank/.docker/docker-compose.yml | 14 - Console._Blank/.dockerignore | 12 - Console._Blank/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 143 --- Console._Blank/.gitignore | 11 - Console._Blank/.kubernetes/configmap.yaml | 8 - Console._Blank/.kubernetes/cronjob.yaml | 50 -- .../Properties/DoNotParallelize.cs | 3 - .../Tests.Console.Blank.csproj | 23 - Console._Blank/Console.Blank.sln | 123 --- .../Console.Blank/Console.Blank.csproj | 43 - Console._Blank/Console.Blank/Dockerfile.Local | 5 - Console._Blank/Console.Blank/Program.cs | 10 - .../Properties/InternalsVisibleTo.cs | 3 - .../appsettings.Development.json | 2 - .../Console.Blank/appsettings.Production.json | 2 - .../Console.Blank/appsettings.Staging.json | 2 - Console._Blank/Console.Blank/appsettings.json | 5 - Console._Blank/Dockerfile | 31 - Console._Blank/README.md | 26 - Web._Blank/.docker/docker-compose.dcproj | 13 - Web._Blank/.docker/docker-compose.yml | 17 - Web._Blank/.dockerignore | 12 - Web._Blank/.github/config/slack.yml | 18 - .../.github/workflows/build-and-deploy.yml | 171 ---- Web._Blank/.gitignore | 11 - Web._Blank/.kubernetes/autoscaler.yaml | 25 - Web._Blank/.kubernetes/configmap.yaml | 8 - Web._Blank/.kubernetes/deployment.yaml | 83 -- Web._Blank/.kubernetes/service.yaml | 12 - .../Properties/DoNotParallelize.cs | 3 - .../Tests.Web.Blank/Tests.Web.Blank.csproj | 23 - Web._Blank/Dockerfile | 35 - Web._Blank/LICENSE | 18 - Web._Blank/README.md | 28 - .../Web.Blank.Models/Web.Blank.Models.csproj | 75 -- Web._Blank/Web.Blank.sln | 140 --- Web._Blank/Web.Blank/App.razor | 11 - Web._Blank/Web.Blank/Dockerfile.Local | 6 - Web._Blank/Web.Blank/Program.cs | 11 - .../Properties/InternalsVisibleTo.cs | 3 - Web._Blank/Web.Blank/Web.Blank.csproj | 36 - .../Web.Blank/appsettings.Development.json | 2 - .../Web.Blank/appsettings.Production.json | 2 - Web._Blank/Web.Blank/appsettings.Staging.json | 2 - Web._Blank/Web.Blank/appsettings.json | 13 - Web._Blank/icon.png | Bin 14103 -> 0 bytes 2772 files changed, 140877 deletions(-) delete mode 100644 Api.ApiClients.Entity/.docker/docker-compose.dcproj delete mode 100644 Api.ApiClients.Entity/.docker/docker-compose.yml delete mode 100644 Api.ApiClients.Entity/.dockerignore delete mode 100644 Api.ApiClients.Entity/.github/config/slack.yml delete mode 100644 Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml delete mode 100644 Api.ApiClients.Entity/.gitignore delete mode 100644 Api.ApiClients.Entity/.kubernetes/autoscaler.yaml delete mode 100644 Api.ApiClients.Entity/.kubernetes/configmap.yaml delete mode 100644 Api.ApiClients.Entity/.kubernetes/deployment.yaml delete mode 100644 Api.ApiClients.Entity/.kubernetes/service.yaml delete mode 100644 Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Properties/DoNotParallelize.cs delete mode 100644 Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Tests.Api.ApiClients.Entity.csproj delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api/NanoApiClient.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Example.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Api.ApiClients.Entity.Service.csproj delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Controllers/ExamplesController.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContext.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContextFactory.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.Designer.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Program.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Development.json delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Production.json delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Staging.json delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.sln delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Api.ApiClients.Entity.csproj delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Controllers/ExamplesController.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Program.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Development.json delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Production.json delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Staging.json delete mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json delete mode 100644 Api.ApiClients.Entity/Dockerfile delete mode 100644 Api.ApiClients.Entity/LICENSE delete mode 100644 Api.ApiClients.Entity/README.md delete mode 100644 Api.ApiClients.Entity/icon.png delete mode 100644 Api.ApiClients.RootLogIn/.docker/docker-compose.dcproj delete mode 100644 Api.ApiClients.RootLogIn/.docker/docker-compose.yml delete mode 100644 Api.ApiClients.RootLogIn/.dockerignore delete mode 100644 Api.ApiClients.RootLogIn/.github/config/slack.yml delete mode 100644 Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml delete mode 100644 Api.ApiClients.RootLogIn/.gitignore delete mode 100644 Api.ApiClients.RootLogIn/.kubernetes/autoscaler.yaml delete mode 100644 Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml delete mode 100644 Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml delete mode 100644 Api.ApiClients.RootLogIn/.kubernetes/service.yaml delete mode 100644 Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Properties/DoNotParallelize.cs delete mode 100644 Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Tests.Api.ApiClients.RootLogIn.csproj delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.sln delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/NanoApiClient.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/AutoAuthenticateRootRequest.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/BaseCustomsRequest.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Api.ApiClients.RootLogin.Service.csproj delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/AuthController.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/CustomsController.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Program.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Development.json delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Production.json delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Staging.json delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Api.ApiClients.RootLogIn.csproj delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Controllers/ExamplesController.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Program.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Development.json delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Production.json delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Staging.json delete mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.json delete mode 100644 Api.ApiClients.RootLogIn/Dockerfile delete mode 100644 Api.ApiClients.RootLogIn/LICENSE delete mode 100644 Api.ApiClients.RootLogIn/README.md delete mode 100644 Api.ApiClients.RootLogIn/icon.png delete mode 100644 Api.ApiClients/.docker/docker-compose.dcproj delete mode 100644 Api.ApiClients/.docker/docker-compose.yml delete mode 100644 Api.ApiClients/.dockerignore delete mode 100644 Api.ApiClients/.github/config/slack.yml delete mode 100644 Api.ApiClients/.github/workflows/build-and-deploy.yml delete mode 100644 Api.ApiClients/.gitignore delete mode 100644 Api.ApiClients/.kubernetes/autoscaler.yaml delete mode 100644 Api.ApiClients/.kubernetes/configmap.yaml delete mode 100644 Api.ApiClients/.kubernetes/deployment.yaml delete mode 100644 Api.ApiClients/.kubernetes/service.yaml delete mode 100644 Api.ApiClients/.tests/Tests.Api.ApiClients/Properties/DoNotParallelize.cs delete mode 100644 Api.ApiClients/.tests/Tests.Api.ApiClients/Tests.Api.ApiClients.csproj delete mode 100644 Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/NanoApiClient.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BadRequestExceptionRequest.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BaseCustomsRequest.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileBodyRequest.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileRequest.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomRequest.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomBody.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomFileBody.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/ProblemDetailsExceptionRequest.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/RequestTracingRequest.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileBodyResponse.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileResponse.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomResponse.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/RequestTracingResponse.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service/Api.ApiClients.Service.csproj delete mode 100644 Api.ApiClients/Api.ApiClients.Service/Controllers/CustomsController.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local delete mode 100644 Api.ApiClients/Api.ApiClients.Service/Program.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ApiClients/Api.ApiClients.Service/appsettings.Development.json delete mode 100644 Api.ApiClients/Api.ApiClients.Service/appsettings.Production.json delete mode 100644 Api.ApiClients/Api.ApiClients.Service/appsettings.Staging.json delete mode 100644 Api.ApiClients/Api.ApiClients.Service/appsettings.json delete mode 100644 Api.ApiClients/Api.ApiClients.sln delete mode 100644 Api.ApiClients/Api.ApiClients/Api.ApiClients.csproj delete mode 100644 Api.ApiClients/Api.ApiClients/Controllers/ExamplesController.cs delete mode 100644 Api.ApiClients/Api.ApiClients/Dockerfile.Local delete mode 100644 Api.ApiClients/Api.ApiClients/Program.cs delete mode 100644 Api.ApiClients/Api.ApiClients/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ApiClients/Api.ApiClients/appsettings.Development.json delete mode 100644 Api.ApiClients/Api.ApiClients/appsettings.Production.json delete mode 100644 Api.ApiClients/Api.ApiClients/appsettings.Staging.json delete mode 100644 Api.ApiClients/Api.ApiClients/appsettings.json delete mode 100644 Api.ApiClients/Dockerfile delete mode 100644 Api.ApiClients/LICENSE delete mode 100644 Api.ApiClients/README.md delete mode 100644 Api.ApiClients/icon.png delete mode 100644 Api.Auth.External.Custom/.docker/docker-compose.dcproj delete mode 100644 Api.Auth.External.Custom/.docker/docker-compose.yml delete mode 100644 Api.Auth.External.Custom/.dockerignore delete mode 100644 Api.Auth.External.Custom/.github/config/slack.yml delete mode 100644 Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Auth.External.Custom/.gitignore delete mode 100644 Api.Auth.External.Custom/.kubernetes/autoscaler.yaml delete mode 100644 Api.Auth.External.Custom/.kubernetes/configmap.yaml delete mode 100644 Api.Auth.External.Custom/.kubernetes/deployment.yaml delete mode 100644 Api.Auth.External.Custom/.kubernetes/service.yaml delete mode 100644 Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Properties/DoNotParallelize.cs delete mode 100644 Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Tests.Api.Auth.External.Custom.csproj delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom.sln delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Api.Auth.External.Custom.csproj delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/AuthController.cs delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/ExamplesController.cs delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Program.cs delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Development.json delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Production.json delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Staging.json delete mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.json delete mode 100644 Api.Auth.External.Custom/Dockerfile delete mode 100644 Api.Auth.External.Custom/LICENSE delete mode 100644 Api.Auth.External.Custom/README.md delete mode 100644 Api.Auth.External.Custom/icon.png delete mode 100644 Api.Auth.RootLogin/.docker/docker-compose.dcproj delete mode 100644 Api.Auth.RootLogin/.docker/docker-compose.yml delete mode 100644 Api.Auth.RootLogin/.dockerignore delete mode 100644 Api.Auth.RootLogin/.github/config/slack.yml delete mode 100644 Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Auth.RootLogin/.gitignore delete mode 100644 Api.Auth.RootLogin/.kubernetes/autoscaler.yaml delete mode 100644 Api.Auth.RootLogin/.kubernetes/configmap.yaml delete mode 100644 Api.Auth.RootLogin/.kubernetes/deployment.yaml delete mode 100644 Api.Auth.RootLogin/.kubernetes/service.yaml delete mode 100644 Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Properties/DoNotParallelize.cs delete mode 100644 Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Tests.Api.Auth.RootLogin.csproj delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin.sln delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Api.Auth.RootLogin.csproj delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/AuthController.cs delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/ExamplesController.cs delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Program.cs delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Development.json delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Production.json delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Staging.json delete mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.json delete mode 100644 Api.Auth.RootLogin/Dockerfile delete mode 100644 Api.Auth.RootLogin/LICENSE delete mode 100644 Api.Auth.RootLogin/README.md delete mode 100644 Api.Auth.RootLogin/icon.png delete mode 100644 Api.Authorization/.docker/docker-compose.dcproj delete mode 100644 Api.Authorization/.docker/docker-compose.yml delete mode 100644 Api.Authorization/.dockerignore delete mode 100644 Api.Authorization/.github/config/slack.yml delete mode 100644 Api.Authorization/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Authorization/.gitignore delete mode 100644 Api.Authorization/.kubernetes/autoscaler.yaml delete mode 100644 Api.Authorization/.kubernetes/configmap.yaml delete mode 100644 Api.Authorization/.kubernetes/deployment.yaml delete mode 100644 Api.Authorization/.kubernetes/service.yaml delete mode 100644 Api.Authorization/.tests/Tests.Api.Authorization/Properties/DoNotParallelize.cs delete mode 100644 Api.Authorization/.tests/Tests.Api.Authorization/Tests.Api.Authorization.csproj delete mode 100644 Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj delete mode 100644 Api.Authorization/Api.Authorization.sln delete mode 100644 Api.Authorization/Api.Authorization/Api.Authorization.csproj delete mode 100644 Api.Authorization/Api.Authorization/Controllers/AuthController.cs delete mode 100644 Api.Authorization/Api.Authorization/Controllers/ExamplesController.cs delete mode 100644 Api.Authorization/Api.Authorization/Dockerfile.Local delete mode 100644 Api.Authorization/Api.Authorization/Extensions/ServiceCollectionExtensions.cs delete mode 100644 Api.Authorization/Api.Authorization/Program.cs delete mode 100644 Api.Authorization/Api.Authorization/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Authorization/Api.Authorization/appsettings.Development.json delete mode 100644 Api.Authorization/Api.Authorization/appsettings.Production.json delete mode 100644 Api.Authorization/Api.Authorization/appsettings.Staging.json delete mode 100644 Api.Authorization/Api.Authorization/appsettings.json delete mode 100644 Api.Authorization/Dockerfile delete mode 100644 Api.Authorization/LICENSE delete mode 100644 Api.Authorization/README.md delete mode 100644 Api.Authorization/icon.png delete mode 100644 Api.ContentNegotiation/.docker/docker-compose.dcproj delete mode 100644 Api.ContentNegotiation/.docker/docker-compose.yml delete mode 100644 Api.ContentNegotiation/.dockerignore delete mode 100644 Api.ContentNegotiation/.github/config/slack.yml delete mode 100644 Api.ContentNegotiation/.github/workflows/build-and-deploy.yml delete mode 100644 Api.ContentNegotiation/.gitignore delete mode 100644 Api.ContentNegotiation/.kubernetes/autoscaler.yaml delete mode 100644 Api.ContentNegotiation/.kubernetes/configmap.yaml delete mode 100644 Api.ContentNegotiation/.kubernetes/deployment.yaml delete mode 100644 Api.ContentNegotiation/.kubernetes/service.yaml delete mode 100644 Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Properties/DoNotParallelize.cs delete mode 100644 Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Tests.Api.ContentNegotiation.csproj delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation.sln delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Api.ContentNegotiation.csproj delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Controllers/ExamplesController.cs delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Program.cs delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Development.json delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Production.json delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Staging.json delete mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/appsettings.json delete mode 100644 Api.ContentNegotiation/Dockerfile delete mode 100644 Api.ContentNegotiation/LICENSE delete mode 100644 Api.ContentNegotiation/README.md delete mode 100644 Api.ContentNegotiation/icon.png delete mode 100644 Api.Cookies/.docker/docker-compose.dcproj delete mode 100644 Api.Cookies/.docker/docker-compose.yml delete mode 100644 Api.Cookies/.dockerignore delete mode 100644 Api.Cookies/.github/config/slack.yml delete mode 100644 Api.Cookies/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Cookies/.gitignore delete mode 100644 Api.Cookies/.kubernetes/autoscaler.yaml delete mode 100644 Api.Cookies/.kubernetes/configmap.yaml delete mode 100644 Api.Cookies/.kubernetes/deployment.yaml delete mode 100644 Api.Cookies/.kubernetes/service.yaml delete mode 100644 Api.Cookies/.tests/Tests.Api.Cookies/Properties/DoNotParallelize.cs delete mode 100644 Api.Cookies/.tests/Tests.Api.Cookies/Tests.Api.Cookies.csproj delete mode 100644 Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj delete mode 100644 Api.Cookies/Api.Cookies.sln delete mode 100644 Api.Cookies/Api.Cookies/Api.Cookies.csproj delete mode 100644 Api.Cookies/Api.Cookies/Controllers/ExamplesController.cs delete mode 100644 Api.Cookies/Api.Cookies/Dockerfile.Local delete mode 100644 Api.Cookies/Api.Cookies/Program.cs delete mode 100644 Api.Cookies/Api.Cookies/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Cookies/Api.Cookies/appsettings.Development.json delete mode 100644 Api.Cookies/Api.Cookies/appsettings.Production.json delete mode 100644 Api.Cookies/Api.Cookies/appsettings.Staging.json delete mode 100644 Api.Cookies/Api.Cookies/appsettings.json delete mode 100644 Api.Cookies/Dockerfile delete mode 100644 Api.Cookies/LICENSE delete mode 100644 Api.Cookies/README.md delete mode 100644 Api.Cookies/icon.png delete mode 100644 Api.CustomConfigSection/.docker/docker-compose.dcproj delete mode 100644 Api.CustomConfigSection/.docker/docker-compose.yml delete mode 100644 Api.CustomConfigSection/.dockerignore delete mode 100644 Api.CustomConfigSection/.github/config/slack.yml delete mode 100644 Api.CustomConfigSection/.github/workflows/build-and-deploy.yml delete mode 100644 Api.CustomConfigSection/.gitignore delete mode 100644 Api.CustomConfigSection/.kubernetes/autoscaler.yaml delete mode 100644 Api.CustomConfigSection/.kubernetes/configmap.yaml delete mode 100644 Api.CustomConfigSection/.kubernetes/deployment.yaml delete mode 100644 Api.CustomConfigSection/.kubernetes/service.yaml delete mode 100644 Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Properties/DoNotParallelize.cs delete mode 100644 Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Tests.Api.CustomConfigSection.csproj delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection.sln delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Api.CustomConfigSection.csproj delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Config/CustomOptions.cs delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Controllers/ExamplesController.cs delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Program.cs delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Properties/InternalsVisibleTo.cs delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Development.json delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Production.json delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Staging.json delete mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/appsettings.json delete mode 100644 Api.CustomConfigSection/Dockerfile delete mode 100644 Api.CustomConfigSection/LICENSE delete mode 100644 Api.CustomConfigSection/README.md delete mode 100644 Api.CustomConfigSection/icon.png delete mode 100644 Api.CustomMiddleware/.docker/docker-compose.dcproj delete mode 100644 Api.CustomMiddleware/.docker/docker-compose.yml delete mode 100644 Api.CustomMiddleware/.dockerignore delete mode 100644 Api.CustomMiddleware/.github/config/slack.yml delete mode 100644 Api.CustomMiddleware/.github/workflows/build-and-deploy.yml delete mode 100644 Api.CustomMiddleware/.gitignore delete mode 100644 Api.CustomMiddleware/.kubernetes/autoscaler.yaml delete mode 100644 Api.CustomMiddleware/.kubernetes/configmap.yaml delete mode 100644 Api.CustomMiddleware/.kubernetes/deployment.yaml delete mode 100644 Api.CustomMiddleware/.kubernetes/service.yaml delete mode 100644 Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Properties/DoNotParallelize.cs delete mode 100644 Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Tests.Api.CustomMiddleware.csproj delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware.sln delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Api.CustomMiddleware.csproj delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Controllers/ExamplesController.cs delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Program.cs delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Properties/InternalsVisibleTo.cs delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Development.json delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Production.json delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Staging.json delete mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/appsettings.json delete mode 100644 Api.CustomMiddleware/Dockerfile delete mode 100644 Api.CustomMiddleware/LICENSE delete mode 100644 Api.CustomMiddleware/README.md delete mode 100644 Api.CustomMiddleware/icon.png delete mode 100644 Api.CustomService/.docker/docker-compose.dcproj delete mode 100644 Api.CustomService/.docker/docker-compose.yml delete mode 100644 Api.CustomService/.dockerignore delete mode 100644 Api.CustomService/.github/config/slack.yml delete mode 100644 Api.CustomService/.github/workflows/build-and-deploy.yml delete mode 100644 Api.CustomService/.gitignore delete mode 100644 Api.CustomService/.kubernetes/autoscaler.yaml delete mode 100644 Api.CustomService/.kubernetes/configmap.yaml delete mode 100644 Api.CustomService/.kubernetes/deployment.yaml delete mode 100644 Api.CustomService/.kubernetes/service.yaml delete mode 100644 Api.CustomService/.tests/Tests.Api.CustomService/Properties/DoNotParallelize.cs delete mode 100644 Api.CustomService/.tests/Tests.Api.CustomService/Tests.Api.CustomService.csproj delete mode 100644 Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj delete mode 100644 Api.CustomService/Api.CustomService.sln delete mode 100644 Api.CustomService/Api.CustomService/Api.CustomService.csproj delete mode 100644 Api.CustomService/Api.CustomService/Controllers/ExamplesController.cs delete mode 100644 Api.CustomService/Api.CustomService/Dockerfile.Local delete mode 100644 Api.CustomService/Api.CustomService/Program.cs delete mode 100644 Api.CustomService/Api.CustomService/Properties/InternalsVisibleTo.cs delete mode 100644 Api.CustomService/Api.CustomService/Services/Abstractions/IExampleService.cs delete mode 100644 Api.CustomService/Api.CustomService/Services/ExampleService.cs delete mode 100644 Api.CustomService/Api.CustomService/appsettings.Development.json delete mode 100644 Api.CustomService/Api.CustomService/appsettings.Production.json delete mode 100644 Api.CustomService/Api.CustomService/appsettings.Staging.json delete mode 100644 Api.CustomService/Api.CustomService/appsettings.json delete mode 100644 Api.CustomService/Dockerfile delete mode 100644 Api.CustomService/LICENSE delete mode 100644 Api.CustomService/README.md delete mode 100644 Api.CustomService/icon.png delete mode 100644 Api.Data.Audit/.docker/docker-compose.dcproj delete mode 100644 Api.Data.Audit/.docker/docker-compose.yml delete mode 100644 Api.Data.Audit/.dockerignore delete mode 100644 Api.Data.Audit/.github/config/slack.yml delete mode 100644 Api.Data.Audit/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.Audit/.gitignore delete mode 100644 Api.Data.Audit/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.Audit/.kubernetes/configmap.yaml delete mode 100644 Api.Data.Audit/.kubernetes/deployment.yaml delete mode 100644 Api.Data.Audit/.kubernetes/service.yaml delete mode 100644 Api.Data.Audit/.tests/Tests.Api.Data.Audit/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.Audit/.tests/Tests.Api.Data.Audit/Tests.Api.Data.Audit.csproj delete mode 100644 Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj delete mode 100644 Api.Data.Audit/Api.Data.Audit.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit.Models/Example.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit.Models/ExampleNavigation.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit.Models/ExampleNoAudit.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit.sln delete mode 100644 Api.Data.Audit/Api.Data.Audit/Api.Data.Audit.csproj delete mode 100644 Api.Data.Audit/Api.Data.Audit/Controllers/AuditController.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Controllers/ExampleNoAuditsController.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Controllers/ExamplesController.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNavigationMapping.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNoAuditMapping.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContext.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Dockerfile.Local delete mode 100644 Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.Designer.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Program.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.Audit/Api.Data.Audit/appsettings.Development.json delete mode 100644 Api.Data.Audit/Api.Data.Audit/appsettings.Production.json delete mode 100644 Api.Data.Audit/Api.Data.Audit/appsettings.Staging.json delete mode 100644 Api.Data.Audit/Api.Data.Audit/appsettings.json delete mode 100644 Api.Data.Audit/Dockerfile delete mode 100644 Api.Data.Audit/LICENSE delete mode 100644 Api.Data.Audit/README.md delete mode 100644 Api.Data.Audit/icon.png delete mode 100644 Api.Data.EntityEvents/.docker/docker-compose.dcproj delete mode 100644 Api.Data.EntityEvents/.docker/docker-compose.yml delete mode 100644 Api.Data.EntityEvents/.docker/init.sql delete mode 100644 Api.Data.EntityEvents/.dockerignore delete mode 100644 Api.Data.EntityEvents/.github/config/slack.yml delete mode 100644 Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.EntityEvents/.gitignore delete mode 100644 Api.Data.EntityEvents/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.EntityEvents/.kubernetes/configmap.yaml delete mode 100644 Api.Data.EntityEvents/.kubernetes/deployment.yaml delete mode 100644 Api.Data.EntityEvents/.kubernetes/service.yaml delete mode 100644 Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Tests.Api.Data.EntityEvents.csproj delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Address.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Criterias/DefaultQueryCriteria.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Customer.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Order.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Owned/ProfileSettings.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Person.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Profile.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Customer.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Api.Data.EntityEvents.Subscriber.csproj delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/Mappings/CustomerMapping.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContext.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.Designer.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Program.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Production.json delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Staging.json delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.json delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.sln delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Api.Data.EntityEvents.csproj delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/AddresssController.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/CustomersController.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/OrdersController.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/ProfilesController.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/AddressMapping.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/CustomerMapping.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/OrderMapping.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/PersonMapping.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/ProfileMapping.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContext.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.Designer.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Program.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Production.json delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Staging.json delete mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.json delete mode 100644 Api.Data.EntityEvents/Dockerfile delete mode 100644 Api.Data.EntityEvents/LICENSE delete mode 100644 Api.Data.EntityEvents/README.md delete mode 100644 Api.Data.EntityEvents/icon.png delete mode 100644 Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.dcproj delete mode 100644 Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml delete mode 100644 Api.Data.Identity.Auth.ApiKey/.dockerignore delete mode 100644 Api.Data.Identity.Auth.ApiKey/.github/config/slack.yml delete mode 100644 Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.Identity.Auth.ApiKey/.gitignore delete mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml delete mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml delete mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/service.yaml delete mode 100644 Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Tests.Api.Data.Identity.Auth.ApiKey.csproj delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Criterias/UserQueryCriteria.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/User.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.sln delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.csproj delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/AuthController.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/UsersController.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/Mappings/UserMapping.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContext.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.Designer.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Program.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Production.json delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Staging.json delete mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.json delete mode 100644 Api.Data.Identity.Auth.ApiKey/Dockerfile delete mode 100644 Api.Data.Identity.Auth.ApiKey/LICENSE delete mode 100644 Api.Data.Identity.Auth.ApiKey/README.md delete mode 100644 Api.Data.Identity.Auth.ApiKey/icon.png delete mode 100644 Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.dcproj delete mode 100644 Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml delete mode 100644 Api.Data.Identity.Auth.External.Custom/.dockerignore delete mode 100644 Api.Data.Identity.Auth.External.Custom/.github/config/slack.yml delete mode 100644 Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.Identity.Auth.External.Custom/.gitignore delete mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml delete mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml delete mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/service.yaml delete mode 100644 Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Tests.Api.Data.Identity.Auth.External.Custom.csproj delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Criterias/UserQueryCriteria.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/User.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.sln delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.csproj delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/AuthController.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/UsersController.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/Mappings/UserMapping.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContext.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.Designer.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Program.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Production.json delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Staging.json delete mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.json delete mode 100644 Api.Data.Identity.Auth.External.Custom/Dockerfile delete mode 100644 Api.Data.Identity.Auth.External.Custom/LICENSE delete mode 100644 Api.Data.Identity.Auth.External.Custom/README.md delete mode 100644 Api.Data.Identity.Auth.External.Custom/icon.png delete mode 100644 Api.Data.Identity.Auth.Jwt/.docker/docker-compose.dcproj delete mode 100644 Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml delete mode 100644 Api.Data.Identity.Auth.Jwt/.dockerignore delete mode 100644 Api.Data.Identity.Auth.Jwt/.github/config/slack.yml delete mode 100644 Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.Identity.Auth.Jwt/.gitignore delete mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml delete mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml delete mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/service.yaml delete mode 100644 Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Tests.Api.Data.Identity.Auth.Jwt.csproj delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Criterias/UserQueryCriteria.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/User.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.sln delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.csproj delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/AuthController.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/UsersController.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/Mappings/UserMapping.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContext.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.Designer.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Program.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Production.json delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Staging.json delete mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.json delete mode 100644 Api.Data.Identity.Auth.Jwt/Dockerfile delete mode 100644 Api.Data.Identity.Auth.Jwt/LICENSE delete mode 100644 Api.Data.Identity.Auth.Jwt/README.md delete mode 100644 Api.Data.Identity.Auth.Jwt/icon.png delete mode 100644 Api.Data.Identity/.docker/docker-compose.dcproj delete mode 100644 Api.Data.Identity/.docker/docker-compose.yml delete mode 100644 Api.Data.Identity/.dockerignore delete mode 100644 Api.Data.Identity/.github/config/slack.yml delete mode 100644 Api.Data.Identity/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.Identity/.gitignore delete mode 100644 Api.Data.Identity/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.Identity/.kubernetes/configmap.yaml delete mode 100644 Api.Data.Identity/.kubernetes/deployment.yaml delete mode 100644 Api.Data.Identity/.kubernetes/service.yaml delete mode 100644 Api.Data.Identity/.tests/Tests.Api.Data.Identity/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.Identity/.tests/Tests.Api.Data.Identity/Tests.Api.Data.Identity.csproj delete mode 100644 Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj delete mode 100644 Api.Data.Identity/Api.Data.Identity.Models/Criterias/UserQueryCriteria.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity.Models/User.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity.sln delete mode 100644 Api.Data.Identity/Api.Data.Identity/Api.Data.Identity.csproj delete mode 100644 Api.Data.Identity/Api.Data.Identity/Controllers/UsersController.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/Data/Mappings/UserMapping.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContext.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/Dockerfile.Local delete mode 100644 Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.Designer.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/Program.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.Identity/Api.Data.Identity/appsettings.Development.json delete mode 100644 Api.Data.Identity/Api.Data.Identity/appsettings.Production.json delete mode 100644 Api.Data.Identity/Api.Data.Identity/appsettings.Staging.json delete mode 100644 Api.Data.Identity/Api.Data.Identity/appsettings.json delete mode 100644 Api.Data.Identity/Dockerfile delete mode 100644 Api.Data.Identity/LICENSE delete mode 100644 Api.Data.Identity/README.md delete mode 100644 Api.Data.Identity/icon.png delete mode 100644 Api.Data.InMemory/.docker/docker-compose.dcproj delete mode 100644 Api.Data.InMemory/.docker/docker-compose.yml delete mode 100644 Api.Data.InMemory/.dockerignore delete mode 100644 Api.Data.InMemory/.github/config/slack.yml delete mode 100644 Api.Data.InMemory/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.InMemory/.gitignore delete mode 100644 Api.Data.InMemory/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.InMemory/.kubernetes/configmap.yaml delete mode 100644 Api.Data.InMemory/.kubernetes/deployment.yaml delete mode 100644 Api.Data.InMemory/.kubernetes/service.yaml delete mode 100644 Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Tests.Api.Data.InMemory.csproj delete mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj delete mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/Example.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatable.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatableAndEditable.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/ExampleDeletable.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/ExampleUpdatable.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory.sln delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Api.Data.InMemory.csproj delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreatableAndEditableController.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreateablesController.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleDeletablesController.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleUpdatablesController.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExamplesController.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/InMemoryDbContext.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableMapping.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleDeletableMapping.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleUpdatableMapping.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Program.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/appsettings.Development.json delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/appsettings.Production.json delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/appsettings.Staging.json delete mode 100644 Api.Data.InMemory/Api.Data.InMemory/appsettings.json delete mode 100644 Api.Data.InMemory/Dockerfile delete mode 100644 Api.Data.InMemory/LICENSE delete mode 100644 Api.Data.InMemory/README.md delete mode 100644 Api.Data.InMemory/icon.png delete mode 100644 Api.Data.LazyLoading/.docker/docker-compose.dcproj delete mode 100644 Api.Data.LazyLoading/.docker/docker-compose.yml delete mode 100644 Api.Data.LazyLoading/.dockerignore delete mode 100644 Api.Data.LazyLoading/.github/config/slack.yml delete mode 100644 Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.LazyLoading/.gitignore delete mode 100644 Api.Data.LazyLoading/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.LazyLoading/.kubernetes/configmap.yaml delete mode 100644 Api.Data.LazyLoading/.kubernetes/deployment.yaml delete mode 100644 Api.Data.LazyLoading/.kubernetes/service.yaml delete mode 100644 Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Tests.Api.Data.LazyLoading.csproj delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Example.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelation.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelationIncluded.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.sln delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Api.Data.LazyLoading.csproj delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Controllers/ExamplesController.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationIncludedMapping.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationMapping.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContext.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.Designer.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Program.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Production.json delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Staging.json delete mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.json delete mode 100644 Api.Data.LazyLoading/Dockerfile delete mode 100644 Api.Data.LazyLoading/LICENSE delete mode 100644 Api.Data.LazyLoading/README.md delete mode 100644 Api.Data.LazyLoading/icon.png delete mode 100644 Api.Data.MySql.Collation/.docker/docker-compose.dcproj delete mode 100644 Api.Data.MySql.Collation/.docker/docker-compose.yml delete mode 100644 Api.Data.MySql.Collation/.dockerignore delete mode 100644 Api.Data.MySql.Collation/.github/config/slack.yml delete mode 100644 Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.MySql.Collation/.gitignore delete mode 100644 Api.Data.MySql.Collation/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.MySql.Collation/.kubernetes/configmap.yaml delete mode 100644 Api.Data.MySql.Collation/.kubernetes/deployment.yaml delete mode 100644 Api.Data.MySql.Collation/.kubernetes/service.yaml delete mode 100644 Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Tests.Api.Data.MySql.Collation.csproj delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Example.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation.sln delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Api.Data.MySql.Collation.csproj delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Controllers/ExamplesController.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContext.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.Designer.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Program.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Production.json delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Staging.json delete mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json delete mode 100644 Api.Data.MySql.Collation/Dockerfile delete mode 100644 Api.Data.MySql.Collation/LICENSE delete mode 100644 Api.Data.MySql.Collation/README.md delete mode 100644 Api.Data.MySql.Collation/icon.png delete mode 100644 Api.Data.MySql.Mappings/.docker/docker-compose.dcproj delete mode 100644 Api.Data.MySql.Mappings/.docker/docker-compose.yml delete mode 100644 Api.Data.MySql.Mappings/.dockerignore delete mode 100644 Api.Data.MySql.Mappings/.github/config/slack.yml delete mode 100644 Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.MySql.Mappings/.gitignore delete mode 100644 Api.Data.MySql.Mappings/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.MySql.Mappings/.kubernetes/configmap.yaml delete mode 100644 Api.Data.MySql.Mappings/.kubernetes/deployment.yaml delete mode 100644 Api.Data.MySql.Mappings/.kubernetes/service.yaml delete mode 100644 Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Tests.Api.Data.MySql.Mappings.csproj delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleNormalizedQueryCriteria.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleJson.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleNormalized.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleOwned.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/Profile.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfilePicture.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfileSettings.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.sln delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.csproj delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleJsonController.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleNormalizedController.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleOwnedController.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleJsonMapping.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleNormalizedMapping.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleOwnedMapping.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/EntityTypeBuilderExtensions.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/OwnedNavigationBuilderExtensions.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContext.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.Designer.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Program.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Production.json delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Staging.json delete mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.json delete mode 100644 Api.Data.MySql.Mappings/Dockerfile delete mode 100644 Api.Data.MySql.Mappings/LICENSE delete mode 100644 Api.Data.MySql.Mappings/README.md delete mode 100644 Api.Data.MySql.Mappings/icon.png delete mode 100644 Api.Data.MySql.Spatial/.docker/docker-compose.dcproj delete mode 100644 Api.Data.MySql.Spatial/.docker/docker-compose.yml delete mode 100644 Api.Data.MySql.Spatial/.dockerignore delete mode 100644 Api.Data.MySql.Spatial/.github/config/slack.yml delete mode 100644 Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.MySql.Spatial/.gitignore delete mode 100644 Api.Data.MySql.Spatial/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.MySql.Spatial/.kubernetes/configmap.yaml delete mode 100644 Api.Data.MySql.Spatial/.kubernetes/deployment.yaml delete mode 100644 Api.Data.MySql.Spatial/.kubernetes/service.yaml delete mode 100644 Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Tests.Api.Data.MySql.Spatial.csproj delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Example.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.sln delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.csproj delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Controllers/ExamplesController.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContext.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.Designer.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Program.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Production.json delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Staging.json delete mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.json delete mode 100644 Api.Data.MySql.Spatial/Dockerfile delete mode 100644 Api.Data.MySql.Spatial/LICENSE delete mode 100644 Api.Data.MySql.Spatial/README.md delete mode 100644 Api.Data.MySql.Spatial/icon.png delete mode 100644 Api.Data.MySql.StoredProcedures/.docker/docker-compose.dcproj delete mode 100644 Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml delete mode 100644 Api.Data.MySql.StoredProcedures/.dockerignore delete mode 100644 Api.Data.MySql.StoredProcedures/.github/config/slack.yml delete mode 100644 Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.MySql.StoredProcedures/.gitignore delete mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml delete mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml delete mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/service.yaml delete mode 100644 Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Tests.Api.Data.MySql.StoredProcedures.csproj delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Example.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/ExampleResult.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.sln delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.csproj delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Controllers/ExamplesController.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Extensions/RepositoryExtensions.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContext.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/StoredProcedures/StoredProcedures.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.Designer.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.Designer.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Program.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Production.json delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Staging.json delete mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.json delete mode 100644 Api.Data.MySql.StoredProcedures/Dockerfile delete mode 100644 Api.Data.MySql.StoredProcedures/LICENSE delete mode 100644 Api.Data.MySql.StoredProcedures/README.md delete mode 100644 Api.Data.MySql.StoredProcedures/icon.png delete mode 100644 Api.Data.MySql.Views/.docker/docker-compose.dcproj delete mode 100644 Api.Data.MySql.Views/.docker/docker-compose.yml delete mode 100644 Api.Data.MySql.Views/.dockerignore delete mode 100644 Api.Data.MySql.Views/.github/config/slack.yml delete mode 100644 Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.MySql.Views/.gitignore delete mode 100644 Api.Data.MySql.Views/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.MySql.Views/.kubernetes/configmap.yaml delete mode 100644 Api.Data.MySql.Views/.kubernetes/deployment.yaml delete mode 100644 Api.Data.MySql.Views/.kubernetes/service.yaml delete mode 100644 Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Tests.Api.Data.MySql.Views.csproj delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Example.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.Models/ExampleView.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.sln delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Api.Data.MySql.Views.csproj delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExampleViewsController.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExamplesController.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleViewMapping.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContext.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Views/ExampleViewDefinition.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.Designer.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.Designer.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Program.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Production.json delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Staging.json delete mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.json delete mode 100644 Api.Data.MySql.Views/Dockerfile delete mode 100644 Api.Data.MySql.Views/LICENSE delete mode 100644 Api.Data.MySql.Views/README.md delete mode 100644 Api.Data.MySql.Views/icon.png delete mode 100644 Api.Data.MySql/.docker/docker-compose.dcproj delete mode 100644 Api.Data.MySql/.docker/docker-compose.yml delete mode 100644 Api.Data.MySql/.dockerignore delete mode 100644 Api.Data.MySql/.github/config/slack.yml delete mode 100644 Api.Data.MySql/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.MySql/.gitignore delete mode 100644 Api.Data.MySql/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.MySql/.kubernetes/configmap.yaml delete mode 100644 Api.Data.MySql/.kubernetes/deployment.yaml delete mode 100644 Api.Data.MySql/.kubernetes/service.yaml delete mode 100644 Api.Data.MySql/.tests/Tests.Api.Data.MySql/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.MySql/.tests/Tests.Api.Data.MySql/Tests.Api.Data.MySql.csproj delete mode 100644 Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj delete mode 100644 Api.Data.MySql/Api.Data.MySql.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql.Models/Example.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatable.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatableAndEditable.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql.Models/ExampleDeletable.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql.Models/ExampleUpdatable.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql.sln delete mode 100644 Api.Data.MySql/Api.Data.MySql/Api.Data.MySql.csproj delete mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreatableAndEditableController.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreateablesController.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExampleDeletablesController.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExampleUpdatablesController.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExamplesController.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableMapping.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleDeletableMapping.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleUpdatableMapping.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContext.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Dockerfile.Local delete mode 100644 Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.Designer.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Program.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.MySql/Api.Data.MySql/appsettings.Development.json delete mode 100644 Api.Data.MySql/Api.Data.MySql/appsettings.Production.json delete mode 100644 Api.Data.MySql/Api.Data.MySql/appsettings.Staging.json delete mode 100644 Api.Data.MySql/Api.Data.MySql/appsettings.json delete mode 100644 Api.Data.MySql/Dockerfile delete mode 100644 Api.Data.MySql/LICENSE delete mode 100644 Api.Data.MySql/README.md delete mode 100644 Api.Data.MySql/icon.png delete mode 100644 Api.Data.PostgreSQL.Spatial/.docker/docker-compose.dcproj delete mode 100644 Api.Data.PostgreSQL.Spatial/.docker/docker-compose.yml delete mode 100644 Api.Data.PostgreSQL.Spatial/.dockerignore delete mode 100644 Api.Data.PostgreSQL.Spatial/.github/config/slack.yml delete mode 100644 Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.PostgreSQL.Spatial/.gitignore delete mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml delete mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml delete mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/service.yaml delete mode 100644 Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Tests.Api.Data.PostgreSQL.Spatial.csproj delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Example.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.sln delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.csproj delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Controllers/ExamplesController.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContext.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContextFactory.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.Designer.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/PostgreSqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Development.json delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Production.json delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Staging.json delete mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.json delete mode 100644 Api.Data.PostgreSQL.Spatial/Dockerfile delete mode 100644 Api.Data.PostgreSQL.Spatial/LICENSE delete mode 100644 Api.Data.PostgreSQL.Spatial/README.md delete mode 100644 Api.Data.PostgreSQL.Spatial/icon.png delete mode 100644 Api.Data.PostgreSQL/.docker/docker-compose.dcproj delete mode 100644 Api.Data.PostgreSQL/.docker/docker-compose.yml delete mode 100644 Api.Data.PostgreSQL/.dockerignore delete mode 100644 Api.Data.PostgreSQL/.github/config/slack.yml delete mode 100644 Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.PostgreSQL/.gitignore delete mode 100644 Api.Data.PostgreSQL/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.PostgreSQL/.kubernetes/configmap.yaml delete mode 100644 Api.Data.PostgreSQL/.kubernetes/deployment.yaml delete mode 100644 Api.Data.PostgreSQL/.kubernetes/service.yaml delete mode 100644 Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Tests.Api.Data.PostgreSQL.csproj delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Example.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatable.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatableAndEditable.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleDeletable.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleUpdatable.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Api.Data.PostgreSQL.csproj delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreatableAndEditableController.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreateablesController.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleDeletablesController.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleUpdatablesController.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExamplesController.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableMapping.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleDeletableMapping.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleUpdatableMapping.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContext.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.Designer.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Development.json delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Production.json delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Staging.json delete mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.json delete mode 100644 Api.Data.PostgreSQL/Dockerfile delete mode 100644 Api.Data.PostgreSQL/LICENSE delete mode 100644 Api.Data.PostgreSQL/README.md delete mode 100644 Api.Data.PostgreSQL/icon.png delete mode 100644 Api.Data.Repository.AutoSave/.docker/docker-compose.dcproj delete mode 100644 Api.Data.Repository.AutoSave/.docker/docker-compose.yml delete mode 100644 Api.Data.Repository.AutoSave/.dockerignore delete mode 100644 Api.Data.Repository.AutoSave/.github/config/slack.yml delete mode 100644 Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.Repository.AutoSave/.gitignore delete mode 100644 Api.Data.Repository.AutoSave/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml delete mode 100644 Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml delete mode 100644 Api.Data.Repository.AutoSave/.kubernetes/service.yaml delete mode 100644 Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Tests.Api.Data.Repository.AutoSave.csproj delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Example.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.sln delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.csproj delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Controllers/ExamplesController.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContext.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.Designer.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Program.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Production.json delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Staging.json delete mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.json delete mode 100644 Api.Data.Repository.AutoSave/Dockerfile delete mode 100644 Api.Data.Repository.AutoSave/LICENSE delete mode 100644 Api.Data.Repository.AutoSave/README.md delete mode 100644 Api.Data.Repository.AutoSave/icon.png delete mode 100644 Api.Data.Repository.Includes/.docker/docker-compose.dcproj delete mode 100644 Api.Data.Repository.Includes/.docker/docker-compose.yml delete mode 100644 Api.Data.Repository.Includes/.dockerignore delete mode 100644 Api.Data.Repository.Includes/.github/config/slack.yml delete mode 100644 Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.Repository.Includes/.gitignore delete mode 100644 Api.Data.Repository.Includes/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.Repository.Includes/.kubernetes/configmap.yaml delete mode 100644 Api.Data.Repository.Includes/.kubernetes/deployment.yaml delete mode 100644 Api.Data.Repository.Includes/.kubernetes/service.yaml delete mode 100644 Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Tests.Api.Data.Repository.Includes.csproj delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Customer.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/CustomerProfile.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Order.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Payment.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.sln delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Api.Data.Repository.Includes.csproj delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/ExamplesController.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerProfileResponse.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerResponse.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/OrderResponse.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/PaymentResponse.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerMapping.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerProfileMapping.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/OrderMapping.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/PaymentMapping.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContext.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.Designer.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Program.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Production.json delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Staging.json delete mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.json delete mode 100644 Api.Data.Repository.Includes/Dockerfile delete mode 100644 Api.Data.Repository.Includes/LICENSE delete mode 100644 Api.Data.Repository.Includes/README.md delete mode 100644 Api.Data.Repository.Includes/icon.png delete mode 100644 Api.Data.SoftDelete/.docker/docker-compose.dcproj delete mode 100644 Api.Data.SoftDelete/.docker/docker-compose.yml delete mode 100644 Api.Data.SoftDelete/.dockerignore delete mode 100644 Api.Data.SoftDelete/.github/config/slack.yml delete mode 100644 Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.SoftDelete/.gitignore delete mode 100644 Api.Data.SoftDelete/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.SoftDelete/.kubernetes/configmap.yaml delete mode 100644 Api.Data.SoftDelete/.kubernetes/deployment.yaml delete mode 100644 Api.Data.SoftDelete/.kubernetes/service.yaml delete mode 100644 Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Tests.Api.Data.SoftDelete.csproj delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Example.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete.sln delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Api.Data.SoftDelete.csproj delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Controllers/ExamplesController.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContext.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.Designer.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Program.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Production.json delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Staging.json delete mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.json delete mode 100644 Api.Data.SoftDelete/Dockerfile delete mode 100644 Api.Data.SoftDelete/LICENSE delete mode 100644 Api.Data.SoftDelete/README.md delete mode 100644 Api.Data.SoftDelete/icon.png delete mode 100644 Api.Data.SqLite/.docker/docker-compose.dcproj delete mode 100644 Api.Data.SqLite/.docker/docker-compose.yml delete mode 100644 Api.Data.SqLite/.dockerignore delete mode 100644 Api.Data.SqLite/.github/config/slack.yml delete mode 100644 Api.Data.SqLite/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.SqLite/.gitignore delete mode 100644 Api.Data.SqLite/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.SqLite/.kubernetes/configmap.yaml delete mode 100644 Api.Data.SqLite/.kubernetes/data-pvc.yaml delete mode 100644 Api.Data.SqLite/.kubernetes/data-storageclass.yaml delete mode 100644 Api.Data.SqLite/.kubernetes/deployment.yaml delete mode 100644 Api.Data.SqLite/.kubernetes/service.yaml delete mode 100644 Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Tests.Api.Data.SqLite.csproj delete mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj delete mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/Example.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatable.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatableAndEditable.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/ExampleDeletable.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/ExampleUpdatable.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite.sln delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Api.Data.SqLite.csproj delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreatableAndEditableController.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreateablesController.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleDeletablesController.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleUpdatablesController.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExamplesController.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableMapping.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleDeletableMapping.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleUpdatableMapping.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContext.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Migrations/20260415151010_Initial.Designer.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Migrations/20260415151010_Initial.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Program.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/appsettings.Production.json delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/appsettings.Staging.json delete mode 100644 Api.Data.SqLite/Api.Data.SqLite/appsettings.json delete mode 100644 Api.Data.SqLite/Dockerfile delete mode 100644 Api.Data.SqLite/LICENSE delete mode 100644 Api.Data.SqLite/README.md delete mode 100644 Api.Data.SqLite/icon.png delete mode 100644 Api.Data.SqlServer.Spatial/.docker/docker-compose.dcproj delete mode 100644 Api.Data.SqlServer.Spatial/.docker/docker-compose.yml delete mode 100644 Api.Data.SqlServer.Spatial/.dockerignore delete mode 100644 Api.Data.SqlServer.Spatial/.github/config/slack.yml delete mode 100644 Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.SqlServer.Spatial/.gitignore delete mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml delete mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml delete mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/service.yaml delete mode 100644 Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Tests.Api.Data.SqlServer.Spatial.csproj delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Example.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.sln delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.csproj delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Controllers/ExamplesController.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContext.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContextFactory.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.Designer.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.Designer.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/SqlServerDbContextModelSnapshot.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Program.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Development.json delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Production.json delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Staging.json delete mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.json delete mode 100644 Api.Data.SqlServer.Spatial/Dockerfile delete mode 100644 Api.Data.SqlServer.Spatial/LICENSE delete mode 100644 Api.Data.SqlServer.Spatial/README.md delete mode 100644 Api.Data.SqlServer.Spatial/icon.png delete mode 100644 Api.Data.SqlServer/.docker/docker-compose.dcproj delete mode 100644 Api.Data.SqlServer/.docker/docker-compose.yml delete mode 100644 Api.Data.SqlServer/.dockerignore delete mode 100644 Api.Data.SqlServer/.github/config/slack.yml delete mode 100644 Api.Data.SqlServer/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.SqlServer/.gitignore delete mode 100644 Api.Data.SqlServer/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.SqlServer/.kubernetes/configmap.yaml delete mode 100644 Api.Data.SqlServer/.kubernetes/deployment.yaml delete mode 100644 Api.Data.SqlServer/.kubernetes/service.yaml delete mode 100644 Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Tests.Api.Data.SqlServer.csproj delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/Example.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatable.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatableAndEditable.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleDeletable.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleUpdatable.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.sln delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Api.Data.SqlServer.csproj delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreatableAndEditableController.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreateablesController.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleDeletablesController.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleUpdatablesController.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExamplesController.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableMapping.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleDeletableMapping.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleUpdatableMapping.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContext.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContextFactory.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.Designer.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Program.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Development.json delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Production.json delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Staging.json delete mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/appsettings.json delete mode 100644 Api.Data.SqlServer/Dockerfile delete mode 100644 Api.Data.SqlServer/LICENSE delete mode 100644 Api.Data.SqlServer/README.md delete mode 100644 Api.Data.SqlServer/icon.png delete mode 100644 Api.Data.Triggers/.docker/docker-compose.dcproj delete mode 100644 Api.Data.Triggers/.docker/docker-compose.yml delete mode 100644 Api.Data.Triggers/.dockerignore delete mode 100644 Api.Data.Triggers/.github/config/slack.yml delete mode 100644 Api.Data.Triggers/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Data.Triggers/.gitignore delete mode 100644 Api.Data.Triggers/.kubernetes/autoscaler.yaml delete mode 100644 Api.Data.Triggers/.kubernetes/configmap.yaml delete mode 100644 Api.Data.Triggers/.kubernetes/deployment.yaml delete mode 100644 Api.Data.Triggers/.kubernetes/service.yaml delete mode 100644 Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Properties/DoNotParallelize.cs delete mode 100644 Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Tests.Api.Data.Triggers.csproj delete mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj delete mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleQueryCriteria.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleTriggerQueryCriteria.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/Example.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/ExampleTrigger.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers.sln delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Api.Data.Triggers.csproj delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Controllers/ExampleTriggersController.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Controllers/ExamplesController.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleMapping.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleTriggerMapping.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContext.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContextFactory.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/Triggers/ExampleTriggers.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.Designer.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Program.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/appsettings.Production.json delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/appsettings.Staging.json delete mode 100644 Api.Data.Triggers/Api.Data.Triggers/appsettings.json delete mode 100644 Api.Data.Triggers/Dockerfile delete mode 100644 Api.Data.Triggers/LICENSE delete mode 100644 Api.Data.Triggers/README.md delete mode 100644 Api.Data.Triggers/icon.png delete mode 100644 Api.Documentation.Nonce/.docker/docker-compose.dcproj delete mode 100644 Api.Documentation.Nonce/.docker/docker-compose.yml delete mode 100644 Api.Documentation.Nonce/.dockerignore delete mode 100644 Api.Documentation.Nonce/.github/config/slack.yml delete mode 100644 Api.Documentation.Nonce/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Documentation.Nonce/.gitignore delete mode 100644 Api.Documentation.Nonce/.kubernetes/autoscaler.yaml delete mode 100644 Api.Documentation.Nonce/.kubernetes/certificate.yaml delete mode 100644 Api.Documentation.Nonce/.kubernetes/configmap.yaml delete mode 100644 Api.Documentation.Nonce/.kubernetes/deployment.yaml delete mode 100644 Api.Documentation.Nonce/.kubernetes/ingress.yaml delete mode 100644 Api.Documentation.Nonce/.kubernetes/service.yaml delete mode 100644 Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Properties/DoNotParallelize.cs delete mode 100644 Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Tests.Api.Documentation.Nonce.csproj delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce.Models/Api.Documentation.Nonce.Models.csproj delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce.sln delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/Api.Documentation.Nonce.csproj delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/Controllers/ExamplesController.cs delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/Dockerfile.Local delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/Program.cs delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Development.json delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Production.json delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Staging.json delete mode 100644 Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.json delete mode 100644 Api.Documentation.Nonce/Dockerfile delete mode 100644 Api.Documentation.Nonce/LICENSE delete mode 100644 Api.Documentation.Nonce/README.md delete mode 100644 Api.Documentation.Nonce/icon.png delete mode 100644 Api.Documentation.Nonce/localhost.pfx delete mode 100644 Api.Documentation/.docker/docker-compose.dcproj delete mode 100644 Api.Documentation/.docker/docker-compose.yml delete mode 100644 Api.Documentation/.dockerignore delete mode 100644 Api.Documentation/.github/config/slack.yml delete mode 100644 Api.Documentation/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Documentation/.gitignore delete mode 100644 Api.Documentation/.kubernetes/autoscaler.yaml delete mode 100644 Api.Documentation/.kubernetes/configmap.yaml delete mode 100644 Api.Documentation/.kubernetes/deployment.yaml delete mode 100644 Api.Documentation/.kubernetes/service.yaml delete mode 100644 Api.Documentation/.tests/Tests.Api.Documentation/Properties/DoNotParallelize.cs delete mode 100644 Api.Documentation/.tests/Tests.Api.Documentation/Tests.Api.Documentation.csproj delete mode 100644 Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj delete mode 100644 Api.Documentation/Api.Documentation.sln delete mode 100644 Api.Documentation/Api.Documentation/Api.Documentation.csproj delete mode 100644 Api.Documentation/Api.Documentation/Controllers/ExamplesController.cs delete mode 100644 Api.Documentation/Api.Documentation/Dockerfile.Local delete mode 100644 Api.Documentation/Api.Documentation/Program.cs delete mode 100644 Api.Documentation/Api.Documentation/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Documentation/Api.Documentation/appsettings.Development.json delete mode 100644 Api.Documentation/Api.Documentation/appsettings.Production.json delete mode 100644 Api.Documentation/Api.Documentation/appsettings.Staging.json delete mode 100644 Api.Documentation/Api.Documentation/appsettings.json delete mode 100644 Api.Documentation/Dockerfile delete mode 100644 Api.Documentation/LICENSE delete mode 100644 Api.Documentation/README.md delete mode 100644 Api.Documentation/icon.png delete mode 100644 Api.ErrorHandling/.docker/docker-compose.dcproj delete mode 100644 Api.ErrorHandling/.docker/docker-compose.yml delete mode 100644 Api.ErrorHandling/.dockerignore delete mode 100644 Api.ErrorHandling/.github/config/slack.yml delete mode 100644 Api.ErrorHandling/.github/workflows/build-and-deploy.yml delete mode 100644 Api.ErrorHandling/.gitignore delete mode 100644 Api.ErrorHandling/.kubernetes/autoscaler.yaml delete mode 100644 Api.ErrorHandling/.kubernetes/configmap.yaml delete mode 100644 Api.ErrorHandling/.kubernetes/deployment.yaml delete mode 100644 Api.ErrorHandling/.kubernetes/service.yaml delete mode 100644 Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Properties/DoNotParallelize.cs delete mode 100644 Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Tests.Api.ErrorHandling.csproj delete mode 100644 Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj delete mode 100644 Api.ErrorHandling/Api.ErrorHandling.sln delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/Api.ErrorHandling.csproj delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/Controllers/ExamplesController.cs delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/Program.cs delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/appsettings.Development.json delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/appsettings.Production.json delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/appsettings.Staging.json delete mode 100644 Api.ErrorHandling/Api.ErrorHandling/appsettings.json delete mode 100644 Api.ErrorHandling/Dockerfile delete mode 100644 Api.ErrorHandling/LICENSE delete mode 100644 Api.ErrorHandling/README.md delete mode 100644 Api.ErrorHandling/icon.png delete mode 100644 Api.Eventing.RabbitMq/.docker/docker-compose.dcproj delete mode 100644 Api.Eventing.RabbitMq/.docker/docker-compose.yml delete mode 100644 Api.Eventing.RabbitMq/.dockerignore delete mode 100644 Api.Eventing.RabbitMq/.github/config/slack.yml delete mode 100644 Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Eventing.RabbitMq/.gitignore delete mode 100644 Api.Eventing.RabbitMq/.kubernetes/autoscaler.yaml delete mode 100644 Api.Eventing.RabbitMq/.kubernetes/configmap.yaml delete mode 100644 Api.Eventing.RabbitMq/.kubernetes/deployment.yaml delete mode 100644 Api.Eventing.RabbitMq/.kubernetes/service.yaml delete mode 100644 Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Properties/DoNotParallelize.cs delete mode 100644 Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Tests.Api.Eventing.RabbitMq.csproj delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.sln delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.csproj delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Controllers/ExamplesController.cs delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandler.cs delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandlerRoutingKey.cs delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModel.cs delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModelRoutingKey.cs delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Program.cs delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Development.json delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Production.json delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Staging.json delete mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.json delete mode 100644 Api.Eventing.RabbitMq/Dockerfile delete mode 100644 Api.Eventing.RabbitMq/LICENSE delete mode 100644 Api.Eventing.RabbitMq/README.md delete mode 100644 Api.Eventing.RabbitMq/icon.png delete mode 100644 Api.HealthChecks/.docker/docker-compose.dcproj delete mode 100644 Api.HealthChecks/.docker/docker-compose.yml delete mode 100644 Api.HealthChecks/.dockerignore delete mode 100644 Api.HealthChecks/.github/config/slack.yml delete mode 100644 Api.HealthChecks/.github/workflows/build-and-deploy.yml delete mode 100644 Api.HealthChecks/.gitignore delete mode 100644 Api.HealthChecks/.kubernetes/autoscaler.yaml delete mode 100644 Api.HealthChecks/.kubernetes/configmap.yaml delete mode 100644 Api.HealthChecks/.kubernetes/deployment.yaml delete mode 100644 Api.HealthChecks/.kubernetes/service.yaml delete mode 100644 Api.HealthChecks/.tests/Tests.Api.HealthChecks/Properties/DoNotParallelize.cs delete mode 100644 Api.HealthChecks/.tests/Tests.Api.HealthChecks/Tests.Api.HealthChecks.csproj delete mode 100644 Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj delete mode 100644 Api.HealthChecks/Api.HealthChecks.sln delete mode 100644 Api.HealthChecks/Api.HealthChecks/Api.HealthChecks.csproj delete mode 100644 Api.HealthChecks/Api.HealthChecks/Controllers/ExamplesController.cs delete mode 100644 Api.HealthChecks/Api.HealthChecks/Dockerfile.Local delete mode 100644 Api.HealthChecks/Api.HealthChecks/Program.cs delete mode 100644 Api.HealthChecks/Api.HealthChecks/Properties/InternalsVisibleTo.cs delete mode 100644 Api.HealthChecks/Api.HealthChecks/appsettings.Development.json delete mode 100644 Api.HealthChecks/Api.HealthChecks/appsettings.Production.json delete mode 100644 Api.HealthChecks/Api.HealthChecks/appsettings.Staging.json delete mode 100644 Api.HealthChecks/Api.HealthChecks/appsettings.json delete mode 100644 Api.HealthChecks/Dockerfile delete mode 100644 Api.HealthChecks/LICENSE delete mode 100644 Api.HealthChecks/README.md delete mode 100644 Api.HealthChecks/icon.png delete mode 100644 Api.Hosting.Http/.docker/docker-compose.dcproj delete mode 100644 Api.Hosting.Http/.docker/docker-compose.yml delete mode 100644 Api.Hosting.Http/.dockerignore delete mode 100644 Api.Hosting.Http/.github/config/slack.yml delete mode 100644 Api.Hosting.Http/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Hosting.Http/.gitignore delete mode 100644 Api.Hosting.Http/.kubernetes/autoscaler.yaml delete mode 100644 Api.Hosting.Http/.kubernetes/configmap.yaml delete mode 100644 Api.Hosting.Http/.kubernetes/deployment.yaml delete mode 100644 Api.Hosting.Http/.kubernetes/service.yaml delete mode 100644 Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Properties/DoNotParallelize.cs delete mode 100644 Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Tests.Api.Hosting.Http.csproj delete mode 100644 Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj delete mode 100644 Api.Hosting.Http/Api.Hosting.Http.sln delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/Api.Hosting.Http.csproj delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/Controllers/ExamplesController.cs delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/Program.cs delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/appsettings.Development.json delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/appsettings.Production.json delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/appsettings.Staging.json delete mode 100644 Api.Hosting.Http/Api.Hosting.Http/appsettings.json delete mode 100644 Api.Hosting.Http/Dockerfile delete mode 100644 Api.Hosting.Http/LICENSE delete mode 100644 Api.Hosting.Http/README.md delete mode 100644 Api.Hosting.Http/icon.png delete mode 100644 Api.Hosting.Https/.docker/docker-compose.dcproj delete mode 100644 Api.Hosting.Https/.docker/docker-compose.yml delete mode 100644 Api.Hosting.Https/.dockerignore delete mode 100644 Api.Hosting.Https/.github/config/slack.yml delete mode 100644 Api.Hosting.Https/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Hosting.Https/.gitignore delete mode 100644 Api.Hosting.Https/.kubernetes/autoscaler.yaml delete mode 100644 Api.Hosting.Https/.kubernetes/certificate.yaml delete mode 100644 Api.Hosting.Https/.kubernetes/configmap.yaml delete mode 100644 Api.Hosting.Https/.kubernetes/deployment.yaml delete mode 100644 Api.Hosting.Https/.kubernetes/ingress.yaml delete mode 100644 Api.Hosting.Https/.kubernetes/service.yaml delete mode 100644 Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Properties/DoNotParallelize.cs delete mode 100644 Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Tests.Api.Hosting.Https.csproj delete mode 100644 Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj delete mode 100644 Api.Hosting.Https/Api.Hosting.Https.sln delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/Api.Hosting.Https.csproj delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/Controllers/ExamplesController.cs delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/Program.cs delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/appsettings.Production.json delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/appsettings.Staging.json delete mode 100644 Api.Hosting.Https/Api.Hosting.Https/appsettings.json delete mode 100644 Api.Hosting.Https/Dockerfile delete mode 100644 Api.Hosting.Https/LICENSE delete mode 100644 Api.Hosting.Https/README.md delete mode 100644 Api.Hosting.Https/icon.png delete mode 100644 Api.Hosting.Https/localhost.pfx delete mode 100644 Api.Hosting.MultipartLimits/.docker/docker-compose.dcproj delete mode 100644 Api.Hosting.MultipartLimits/.docker/docker-compose.yml delete mode 100644 Api.Hosting.MultipartLimits/.dockerignore delete mode 100644 Api.Hosting.MultipartLimits/.github/config/slack.yml delete mode 100644 Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Hosting.MultipartLimits/.gitignore delete mode 100644 Api.Hosting.MultipartLimits/.kubernetes/autoscaler.yaml delete mode 100644 Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml delete mode 100644 Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml delete mode 100644 Api.Hosting.MultipartLimits/.kubernetes/service.yaml delete mode 100644 Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Properties/DoNotParallelize.cs delete mode 100644 Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Tests.Api.Hosting.MultipartLimits.csproj delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.sln delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.csproj delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Controllers/ExamplesController.cs delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Program.cs delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Development.json delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Production.json delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Staging.json delete mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.json delete mode 100644 Api.Hosting.MultipartLimits/Dockerfile delete mode 100644 Api.Hosting.MultipartLimits/LICENSE delete mode 100644 Api.Hosting.MultipartLimits/README.md delete mode 100644 Api.Hosting.MultipartLimits/icon.png delete mode 100644 Api.Localization/.docker/docker-compose.dcproj delete mode 100644 Api.Localization/.docker/docker-compose.yml delete mode 100644 Api.Localization/.dockerignore delete mode 100644 Api.Localization/.github/config/slack.yml delete mode 100644 Api.Localization/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Localization/.gitignore delete mode 100644 Api.Localization/.kubernetes/autoscaler.yaml delete mode 100644 Api.Localization/.kubernetes/configmap.yaml delete mode 100644 Api.Localization/.kubernetes/deployment.yaml delete mode 100644 Api.Localization/.kubernetes/service.yaml delete mode 100644 Api.Localization/.tests/Tests.Api.Localization/Properties/DoNotParallelize.cs delete mode 100644 Api.Localization/.tests/Tests.Api.Localization/Tests.Api.Localization.csproj delete mode 100644 Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj delete mode 100644 Api.Localization/Api.Localization.sln delete mode 100644 Api.Localization/Api.Localization/Api.Localization.csproj delete mode 100644 Api.Localization/Api.Localization/Controllers/ExamplesController.cs delete mode 100644 Api.Localization/Api.Localization/Dockerfile.Local delete mode 100644 Api.Localization/Api.Localization/Program.cs delete mode 100644 Api.Localization/Api.Localization/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Localization/Api.Localization/appsettings.Development.json delete mode 100644 Api.Localization/Api.Localization/appsettings.Production.json delete mode 100644 Api.Localization/Api.Localization/appsettings.Staging.json delete mode 100644 Api.Localization/Api.Localization/appsettings.json delete mode 100644 Api.Localization/Dockerfile delete mode 100644 Api.Localization/LICENSE delete mode 100644 Api.Localization/README.md delete mode 100644 Api.Localization/icon.png delete mode 100644 Api.Logging.Log4Net/.docker/docker-compose.dcproj delete mode 100644 Api.Logging.Log4Net/.docker/docker-compose.yml delete mode 100644 Api.Logging.Log4Net/.dockerignore delete mode 100644 Api.Logging.Log4Net/.github/config/slack.yml delete mode 100644 Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Logging.Log4Net/.gitignore delete mode 100644 Api.Logging.Log4Net/.kubernetes/autoscaler.yaml delete mode 100644 Api.Logging.Log4Net/.kubernetes/configmap.yaml delete mode 100644 Api.Logging.Log4Net/.kubernetes/deployment.yaml delete mode 100644 Api.Logging.Log4Net/.kubernetes/service.yaml delete mode 100644 Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Properties/DoNotParallelize.cs delete mode 100644 Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Tests.Api.Logging.Log4Net.csproj delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net.sln delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Api.Logging.Log4Net.csproj delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Controllers/ExamplesController.cs delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Program.cs delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Development.json delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Production.json delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Staging.json delete mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.json delete mode 100644 Api.Logging.Log4Net/Dockerfile delete mode 100644 Api.Logging.Log4Net/LICENSE delete mode 100644 Api.Logging.Log4Net/README.md delete mode 100644 Api.Logging.Log4Net/icon.png delete mode 100644 Api.Logging.Microsoft/.docker/docker-compose.dcproj delete mode 100644 Api.Logging.Microsoft/.docker/docker-compose.yml delete mode 100644 Api.Logging.Microsoft/.dockerignore delete mode 100644 Api.Logging.Microsoft/.github/config/slack.yml delete mode 100644 Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Logging.Microsoft/.gitignore delete mode 100644 Api.Logging.Microsoft/.kubernetes/autoscaler.yaml delete mode 100644 Api.Logging.Microsoft/.kubernetes/configmap.yaml delete mode 100644 Api.Logging.Microsoft/.kubernetes/deployment.yaml delete mode 100644 Api.Logging.Microsoft/.kubernetes/service.yaml delete mode 100644 Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Properties/DoNotParallelize.cs delete mode 100644 Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Tests.Api.Logging.Microsoft.csproj delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft.sln delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Api.Logging.Microsoft.csproj delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Controllers/ExamplesController.cs delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Program.cs delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Development.json delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Production.json delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Staging.json delete mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.json delete mode 100644 Api.Logging.Microsoft/Dockerfile delete mode 100644 Api.Logging.Microsoft/LICENSE delete mode 100644 Api.Logging.Microsoft/README.md delete mode 100644 Api.Logging.Microsoft/icon.png delete mode 100644 Api.Logging.NLog/.docker/docker-compose.dcproj delete mode 100644 Api.Logging.NLog/.docker/docker-compose.yml delete mode 100644 Api.Logging.NLog/.dockerignore delete mode 100644 Api.Logging.NLog/.github/config/slack.yml delete mode 100644 Api.Logging.NLog/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Logging.NLog/.gitignore delete mode 100644 Api.Logging.NLog/.kubernetes/autoscaler.yaml delete mode 100644 Api.Logging.NLog/.kubernetes/configmap.yaml delete mode 100644 Api.Logging.NLog/.kubernetes/deployment.yaml delete mode 100644 Api.Logging.NLog/.kubernetes/service.yaml delete mode 100644 Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Properties/DoNotParallelize.cs delete mode 100644 Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Tests.Api.Logging.NLog.csproj delete mode 100644 Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj delete mode 100644 Api.Logging.NLog/Api.Logging.NLog.sln delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/Api.Logging.NLog.csproj delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/Controllers/ExamplesController.cs delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/Program.cs delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/appsettings.Development.json delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/appsettings.Production.json delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/appsettings.Staging.json delete mode 100644 Api.Logging.NLog/Api.Logging.NLog/appsettings.json delete mode 100644 Api.Logging.NLog/Dockerfile delete mode 100644 Api.Logging.NLog/LICENSE delete mode 100644 Api.Logging.NLog/README.md delete mode 100644 Api.Logging.NLog/icon.png delete mode 100644 Api.Logging.Serilog/.docker/docker-compose.dcproj delete mode 100644 Api.Logging.Serilog/.docker/docker-compose.yml delete mode 100644 Api.Logging.Serilog/.dockerignore delete mode 100644 Api.Logging.Serilog/.github/config/slack.yml delete mode 100644 Api.Logging.Serilog/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Logging.Serilog/.gitignore delete mode 100644 Api.Logging.Serilog/.kubernetes/autoscaler.yaml delete mode 100644 Api.Logging.Serilog/.kubernetes/configmap.yaml delete mode 100644 Api.Logging.Serilog/.kubernetes/deployment.yaml delete mode 100644 Api.Logging.Serilog/.kubernetes/service.yaml delete mode 100644 Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Properties/DoNotParallelize.cs delete mode 100644 Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Tests.Api.Logging.Serilog.csproj delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog.sln delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Api.Logging.Serilog.csproj delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Controllers/ExamplesController.cs delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Program.cs delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Development.json delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Production.json delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Staging.json delete mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/appsettings.json delete mode 100644 Api.Logging.Serilog/Dockerfile delete mode 100644 Api.Logging.Serilog/LICENSE delete mode 100644 Api.Logging.Serilog/README.md delete mode 100644 Api.Logging.Serilog/icon.png delete mode 100644 Api.MultipartJson/.docker/docker-compose.dcproj delete mode 100644 Api.MultipartJson/.docker/docker-compose.yml delete mode 100644 Api.MultipartJson/.dockerignore delete mode 100644 Api.MultipartJson/.github/config/slack.yml delete mode 100644 Api.MultipartJson/.github/workflows/build-and-deploy.yml delete mode 100644 Api.MultipartJson/.gitignore delete mode 100644 Api.MultipartJson/.kubernetes/autoscaler.yaml delete mode 100644 Api.MultipartJson/.kubernetes/configmap.yaml delete mode 100644 Api.MultipartJson/.kubernetes/deployment.yaml delete mode 100644 Api.MultipartJson/.kubernetes/service.yaml delete mode 100644 Api.MultipartJson/.tests/Tests.Api.MultipartJson/Properties/DoNotParallelize.cs delete mode 100644 Api.MultipartJson/.tests/Tests.Api.MultipartJson/Tests.Api.MultipartJson.csproj delete mode 100644 Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj delete mode 100644 Api.MultipartJson/Api.MultipartJson.sln delete mode 100644 Api.MultipartJson/Api.MultipartJson/Api.MultipartJson.csproj delete mode 100644 Api.MultipartJson/Api.MultipartJson/Controllers/ExamplesController.cs delete mode 100644 Api.MultipartJson/Api.MultipartJson/Controllers/Requests/JsonBody.cs delete mode 100644 Api.MultipartJson/Api.MultipartJson/Dockerfile.Local delete mode 100644 Api.MultipartJson/Api.MultipartJson/Program.cs delete mode 100644 Api.MultipartJson/Api.MultipartJson/Properties/InternalsVisibleTo.cs delete mode 100644 Api.MultipartJson/Api.MultipartJson/appsettings.Development.json delete mode 100644 Api.MultipartJson/Api.MultipartJson/appsettings.Production.json delete mode 100644 Api.MultipartJson/Api.MultipartJson/appsettings.Staging.json delete mode 100644 Api.MultipartJson/Api.MultipartJson/appsettings.json delete mode 100644 Api.MultipartJson/Dockerfile delete mode 100644 Api.MultipartJson/LICENSE delete mode 100644 Api.MultipartJson/README.md delete mode 100644 Api.MultipartJson/icon.png delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.dockerignore delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.gitignore delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/certificate.yaml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/ingress.yaml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.sln delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.csproj delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Program.cs delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.json delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/wwwroot/csp-violation.html delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/LICENSE delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/README.md delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/icon.png delete mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/localhost.pfx delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.dockerignore delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.gitignore delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Tests.Api.PolicyHeaders.ContentTypeOptions.csproj delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.sln delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.csproj delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Program.cs delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.json delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation-wrong-type.txt delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation.html delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/Dockerfile delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/LICENSE delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/README.md delete mode 100644 Api.PolicyHeaders.ContentTypeOptions/icon.png delete mode 100644 Api.PolicyHeaders.Cors/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.Cors/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.Cors/.dockerignore delete mode 100644 Api.PolicyHeaders.Cors/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.Cors/.gitignore delete mode 100644 Api.PolicyHeaders.Cors/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.Cors/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Tests.Api.PolicyHeaders.Cors.csproj delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.sln delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.csproj delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Program.cs delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.json delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-credetntials-not-alloweed.html delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-header-not-allowed.html delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-method-not-allowed.html delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-origin-not-allowed.html delete mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-preflight-blocked.html delete mode 100644 Api.PolicyHeaders.Cors/Dockerfile delete mode 100644 Api.PolicyHeaders.Cors/LICENSE delete mode 100644 Api.PolicyHeaders.Cors/README.md delete mode 100644 Api.PolicyHeaders.Cors/icon.png delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.dockerignore delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.gitignore delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Tests.Api.PolicyHeaders.ForwardedHeaders.csproj delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.sln delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.csproj delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Program.cs delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.json delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/Dockerfile delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/LICENSE delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/README.md delete mode 100644 Api.PolicyHeaders.ForwardedHeaders/icon.png delete mode 100644 Api.PolicyHeaders.FrameOptions/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.FrameOptions/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.FrameOptions/.dockerignore delete mode 100644 Api.PolicyHeaders.FrameOptions/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.FrameOptions/.gitignore delete mode 100644 Api.PolicyHeaders.FrameOptions/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.FrameOptions/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Tests.Api.PolicyHeaders.FrameOptions.csproj delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.sln delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.csproj delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Program.cs delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.json delete mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/wwwroot/frame-options-violation.html delete mode 100644 Api.PolicyHeaders.FrameOptions/Dockerfile delete mode 100644 Api.PolicyHeaders.FrameOptions/LICENSE delete mode 100644 Api.PolicyHeaders.FrameOptions/README.md delete mode 100644 Api.PolicyHeaders.FrameOptions/icon.png delete mode 100644 Api.PolicyHeaders.Hsts/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.Hsts/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.Hsts/.dockerignore delete mode 100644 Api.PolicyHeaders.Hsts/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.Hsts/.gitignore delete mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/certificate.yaml delete mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/ingress.yaml delete mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Tests.Api.PolicyHeaders.Hsts.csproj delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.sln delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.csproj delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Program.cs delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.json delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/wwwroot/hsts-violation-image.png delete mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/wwwroot/hsts-violation.html delete mode 100644 Api.PolicyHeaders.Hsts/Dockerfile delete mode 100644 Api.PolicyHeaders.Hsts/LICENSE delete mode 100644 Api.PolicyHeaders.Hsts/README.md delete mode 100644 Api.PolicyHeaders.Hsts/icon.png delete mode 100644 Api.PolicyHeaders.Hsts/localhost.pfx delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.dockerignore delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.gitignore delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Tests.Api.PolicyHeaders.ReferrerPolicy.csproj delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.sln delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.csproj delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Program.cs delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.json delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/wwwroot/referrer-policy-violation.html delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/Dockerfile delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/LICENSE delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/README.md delete mode 100644 Api.PolicyHeaders.ReferrerPolicy/icon.png delete mode 100644 Api.PolicyHeaders.Robots/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.Robots/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.Robots/.dockerignore delete mode 100644 Api.PolicyHeaders.Robots/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.Robots/.gitignore delete mode 100644 Api.PolicyHeaders.Robots/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.Robots/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Tests.Api.PolicyHeaders.Robots.csproj delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.sln delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.csproj delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Program.cs delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.json delete mode 100644 Api.PolicyHeaders.Robots/Dockerfile delete mode 100644 Api.PolicyHeaders.Robots/LICENSE delete mode 100644 Api.PolicyHeaders.Robots/README.md delete mode 100644 Api.PolicyHeaders.Robots/icon.png delete mode 100644 Api.PolicyHeaders.XssProtection/.docker/docker-compose.dcproj delete mode 100644 Api.PolicyHeaders.XssProtection/.docker/docker-compose.yml delete mode 100644 Api.PolicyHeaders.XssProtection/.dockerignore delete mode 100644 Api.PolicyHeaders.XssProtection/.github/config/slack.yml delete mode 100644 Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml delete mode 100644 Api.PolicyHeaders.XssProtection/.gitignore delete mode 100644 Api.PolicyHeaders.XssProtection/.kubernetes/autoscaler.yaml delete mode 100644 Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml delete mode 100644 Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml delete mode 100644 Api.PolicyHeaders.XssProtection/.kubernetes/service.yaml delete mode 100644 Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Properties/DoNotParallelize.cs delete mode 100644 Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Tests.Api.PolicyHeaders.XssProtection.csproj delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.sln delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.csproj delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Controllers/ExamplesController.cs delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Program.cs delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Properties/InternalsVisibleTo.cs delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Development.json delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Production.json delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Staging.json delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.json delete mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/wwwroot/xss-violation.html delete mode 100644 Api.PolicyHeaders.XssProtection/Dockerfile delete mode 100644 Api.PolicyHeaders.XssProtection/LICENSE delete mode 100644 Api.PolicyHeaders.XssProtection/README.md delete mode 100644 Api.PolicyHeaders.XssProtection/icon.png delete mode 100644 Api.RequestTracing/.docker/docker-compose.dcproj delete mode 100644 Api.RequestTracing/.docker/docker-compose.yml delete mode 100644 Api.RequestTracing/.dockerignore delete mode 100644 Api.RequestTracing/.github/config/slack.yml delete mode 100644 Api.RequestTracing/.github/workflows/build-and-deploy.yml delete mode 100644 Api.RequestTracing/.gitignore delete mode 100644 Api.RequestTracing/.kubernetes/autoscaler.yaml delete mode 100644 Api.RequestTracing/.kubernetes/configmap.yaml delete mode 100644 Api.RequestTracing/.kubernetes/deployment.yaml delete mode 100644 Api.RequestTracing/.kubernetes/service.yaml delete mode 100644 Api.RequestTracing/.tests/Tests.Api.RequestTracing/Properties/DoNotParallelize.cs delete mode 100644 Api.RequestTracing/.tests/Tests.Api.RequestTracing/Tests.Api.RequestTracing.csproj delete mode 100644 Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj delete mode 100644 Api.RequestTracing/Api.RequestTracing.sln delete mode 100644 Api.RequestTracing/Api.RequestTracing/Api.RequestTracing.csproj delete mode 100644 Api.RequestTracing/Api.RequestTracing/Controllers/ExamplesController.cs delete mode 100644 Api.RequestTracing/Api.RequestTracing/Dockerfile.Local delete mode 100644 Api.RequestTracing/Api.RequestTracing/Program.cs delete mode 100644 Api.RequestTracing/Api.RequestTracing/Properties/InternalsVisibleTo.cs delete mode 100644 Api.RequestTracing/Api.RequestTracing/appsettings.Development.json delete mode 100644 Api.RequestTracing/Api.RequestTracing/appsettings.Production.json delete mode 100644 Api.RequestTracing/Api.RequestTracing/appsettings.Staging.json delete mode 100644 Api.RequestTracing/Api.RequestTracing/appsettings.json delete mode 100644 Api.RequestTracing/Dockerfile delete mode 100644 Api.RequestTracing/LICENSE delete mode 100644 Api.RequestTracing/README.md delete mode 100644 Api.RequestTracing/icon.png delete mode 100644 Api.ResponseCache/.docker/docker-compose.dcproj delete mode 100644 Api.ResponseCache/.docker/docker-compose.yml delete mode 100644 Api.ResponseCache/.dockerignore delete mode 100644 Api.ResponseCache/.github/config/slack.yml delete mode 100644 Api.ResponseCache/.github/workflows/build-and-deploy.yml delete mode 100644 Api.ResponseCache/.gitignore delete mode 100644 Api.ResponseCache/.kubernetes/autoscaler.yaml delete mode 100644 Api.ResponseCache/.kubernetes/configmap.yaml delete mode 100644 Api.ResponseCache/.kubernetes/deployment.yaml delete mode 100644 Api.ResponseCache/.kubernetes/service.yaml delete mode 100644 Api.ResponseCache/.tests/Tests.Api.ResponseCache/Properties/DoNotParallelize.cs delete mode 100644 Api.ResponseCache/.tests/Tests.Api.ResponseCache/Tests.Api.ResponseCache.csproj delete mode 100644 Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj delete mode 100644 Api.ResponseCache/Api.ResponseCache.sln delete mode 100644 Api.ResponseCache/Api.ResponseCache/Api.ResponseCache.csproj delete mode 100644 Api.ResponseCache/Api.ResponseCache/Controllers/ExamplesController.cs delete mode 100644 Api.ResponseCache/Api.ResponseCache/Dockerfile.Local delete mode 100644 Api.ResponseCache/Api.ResponseCache/Program.cs delete mode 100644 Api.ResponseCache/Api.ResponseCache/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ResponseCache/Api.ResponseCache/appsettings.Development.json delete mode 100644 Api.ResponseCache/Api.ResponseCache/appsettings.Production.json delete mode 100644 Api.ResponseCache/Api.ResponseCache/appsettings.Staging.json delete mode 100644 Api.ResponseCache/Api.ResponseCache/appsettings.json delete mode 100644 Api.ResponseCache/Dockerfile delete mode 100644 Api.ResponseCache/LICENSE delete mode 100644 Api.ResponseCache/README.md delete mode 100644 Api.ResponseCache/icon.png delete mode 100644 Api.ResponseCompression/.docker/docker-compose.dcproj delete mode 100644 Api.ResponseCompression/.docker/docker-compose.yml delete mode 100644 Api.ResponseCompression/.dockerignore delete mode 100644 Api.ResponseCompression/.github/config/slack.yml delete mode 100644 Api.ResponseCompression/.github/workflows/build-and-deploy.yml delete mode 100644 Api.ResponseCompression/.gitignore delete mode 100644 Api.ResponseCompression/.kubernetes/autoscaler.yaml delete mode 100644 Api.ResponseCompression/.kubernetes/configmap.yaml delete mode 100644 Api.ResponseCompression/.kubernetes/deployment.yaml delete mode 100644 Api.ResponseCompression/.kubernetes/service.yaml delete mode 100644 Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Properties/DoNotParallelize.cs delete mode 100644 Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Tests.Api.ResponseCompression.csproj delete mode 100644 Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj delete mode 100644 Api.ResponseCompression/Api.ResponseCompression.sln delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/Api.ResponseCompression.csproj delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/Controllers/ExamplesController.cs delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/Program.cs delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/Properties/InternalsVisibleTo.cs delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/appsettings.Development.json delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/appsettings.Production.json delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/appsettings.Staging.json delete mode 100644 Api.ResponseCompression/Api.ResponseCompression/appsettings.json delete mode 100644 Api.ResponseCompression/Dockerfile delete mode 100644 Api.ResponseCompression/LICENSE delete mode 100644 Api.ResponseCompression/README.md delete mode 100644 Api.ResponseCompression/icon.png delete mode 100644 Api.Session/.docker/docker-compose.dcproj delete mode 100644 Api.Session/.docker/docker-compose.yml delete mode 100644 Api.Session/.dockerignore delete mode 100644 Api.Session/.github/config/slack.yml delete mode 100644 Api.Session/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Session/.gitignore delete mode 100644 Api.Session/.kubernetes/autoscaler.yaml delete mode 100644 Api.Session/.kubernetes/configmap.yaml delete mode 100644 Api.Session/.kubernetes/deployment.yaml delete mode 100644 Api.Session/.kubernetes/service.yaml delete mode 100644 Api.Session/.tests/Tests.Api.Session/Properties/DoNotParallelize.cs delete mode 100644 Api.Session/.tests/Tests.Api.Session/Tests.Api.Session.csproj delete mode 100644 Api.Session/Api.Session.Models/Api.Session.Models.csproj delete mode 100644 Api.Session/Api.Session.sln delete mode 100644 Api.Session/Api.Session/Api.Session.csproj delete mode 100644 Api.Session/Api.Session/Controllers/ExamplesController.cs delete mode 100644 Api.Session/Api.Session/Dockerfile.Local delete mode 100644 Api.Session/Api.Session/Program.cs delete mode 100644 Api.Session/Api.Session/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Session/Api.Session/appsettings.Development.json delete mode 100644 Api.Session/Api.Session/appsettings.Production.json delete mode 100644 Api.Session/Api.Session/appsettings.Staging.json delete mode 100644 Api.Session/Api.Session/appsettings.json delete mode 100644 Api.Session/Dockerfile delete mode 100644 Api.Session/LICENSE delete mode 100644 Api.Session/README.md delete mode 100644 Api.Session/icon.png delete mode 100644 Api.StartupTasks/.docker/docker-compose.dcproj delete mode 100644 Api.StartupTasks/.docker/docker-compose.yml delete mode 100644 Api.StartupTasks/.dockerignore delete mode 100644 Api.StartupTasks/.github/config/slack.yml delete mode 100644 Api.StartupTasks/.github/workflows/build-and-deploy.yml delete mode 100644 Api.StartupTasks/.gitignore delete mode 100644 Api.StartupTasks/.kubernetes/autoscaler.yaml delete mode 100644 Api.StartupTasks/.kubernetes/configmap.yaml delete mode 100644 Api.StartupTasks/.kubernetes/deployment.yaml delete mode 100644 Api.StartupTasks/.kubernetes/service.yaml delete mode 100644 Api.StartupTasks/.tests/Tests.Api.StartupTasks/Properties/DoNotParallelize.cs delete mode 100644 Api.StartupTasks/.tests/Tests.Api.StartupTasks/Tests.Api.StartupTasks.csproj delete mode 100644 Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj delete mode 100644 Api.StartupTasks/Api.StartupTasks.sln delete mode 100644 Api.StartupTasks/Api.StartupTasks/Api.StartupTasks.csproj delete mode 100644 Api.StartupTasks/Api.StartupTasks/Controllers/ExamplesController.cs delete mode 100644 Api.StartupTasks/Api.StartupTasks/Dockerfile.Local delete mode 100644 Api.StartupTasks/Api.StartupTasks/Program.cs delete mode 100644 Api.StartupTasks/Api.StartupTasks/Properties/InternalsVisibleTo.cs delete mode 100644 Api.StartupTasks/Api.StartupTasks/Startup/ExampleStartupTask.cs delete mode 100644 Api.StartupTasks/Api.StartupTasks/appsettings.Development.json delete mode 100644 Api.StartupTasks/Api.StartupTasks/appsettings.Production.json delete mode 100644 Api.StartupTasks/Api.StartupTasks/appsettings.Staging.json delete mode 100644 Api.StartupTasks/Api.StartupTasks/appsettings.json delete mode 100644 Api.StartupTasks/Dockerfile delete mode 100644 Api.StartupTasks/LICENSE delete mode 100644 Api.StartupTasks/README.md delete mode 100644 Api.StartupTasks/icon.png delete mode 100644 Api.StaticFiles/.docker/docker-compose.dcproj delete mode 100644 Api.StaticFiles/.docker/docker-compose.yml delete mode 100644 Api.StaticFiles/.dockerignore delete mode 100644 Api.StaticFiles/.github/config/slack.yml delete mode 100644 Api.StaticFiles/.github/workflows/build-and-deploy.yml delete mode 100644 Api.StaticFiles/.gitignore delete mode 100644 Api.StaticFiles/.kubernetes/autoscaler.yaml delete mode 100644 Api.StaticFiles/.kubernetes/configmap.yaml delete mode 100644 Api.StaticFiles/.kubernetes/deployment.yaml delete mode 100644 Api.StaticFiles/.kubernetes/service.yaml delete mode 100644 Api.StaticFiles/.tests/Tests.Api.StaticFiles/Properties/DoNotParallelize.cs delete mode 100644 Api.StaticFiles/.tests/Tests.Api.StaticFiles/Tests.Api.StaticFiles.csproj delete mode 100644 Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj delete mode 100644 Api.StaticFiles/Api.StaticFiles.sln delete mode 100644 Api.StaticFiles/Api.StaticFiles/Api.StaticFiles.csproj delete mode 100644 Api.StaticFiles/Api.StaticFiles/Controllers/ExamplesController.cs delete mode 100644 Api.StaticFiles/Api.StaticFiles/Dockerfile.Local delete mode 100644 Api.StaticFiles/Api.StaticFiles/Program.cs delete mode 100644 Api.StaticFiles/Api.StaticFiles/Properties/InternalsVisibleTo.cs delete mode 100644 Api.StaticFiles/Api.StaticFiles/appsettings.Development.json delete mode 100644 Api.StaticFiles/Api.StaticFiles/appsettings.Production.json delete mode 100644 Api.StaticFiles/Api.StaticFiles/appsettings.Staging.json delete mode 100644 Api.StaticFiles/Api.StaticFiles/appsettings.json delete mode 100644 Api.StaticFiles/Api.StaticFiles/wwwroot/fonts/open-sans-v44-latin-regular.woff2 delete mode 100644 Api.StaticFiles/Api.StaticFiles/wwwroot/images/image.jpg delete mode 100644 Api.StaticFiles/Api.StaticFiles/wwwroot/index.html delete mode 100644 Api.StaticFiles/Dockerfile delete mode 100644 Api.StaticFiles/LICENSE delete mode 100644 Api.StaticFiles/README.md delete mode 100644 Api.StaticFiles/icon.png delete mode 100644 Api.Storage.Azure/.docker/docker-compose.dcproj delete mode 100644 Api.Storage.Azure/.docker/docker-compose.yml delete mode 100644 Api.Storage.Azure/.dockerignore delete mode 100644 Api.Storage.Azure/.github/config/slack.yml delete mode 100644 Api.Storage.Azure/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Storage.Azure/.gitignore delete mode 100644 Api.Storage.Azure/.kubernetes/autoscaler.yaml delete mode 100644 Api.Storage.Azure/.kubernetes/configmap.yaml delete mode 100644 Api.Storage.Azure/.kubernetes/deployment.yaml delete mode 100644 Api.Storage.Azure/.kubernetes/service.yaml delete mode 100644 Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Properties/DoNotParallelize.cs delete mode 100644 Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Tests.Api.Storage.Azure.csproj delete mode 100644 Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj delete mode 100644 Api.Storage.Azure/Api.Storage.Azure.sln delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/Api.Storage.Azure.csproj delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/Controllers/ExamplesController.cs delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/Program.cs delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/appsettings.Development.json delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/appsettings.Production.json delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/appsettings.Staging.json delete mode 100644 Api.Storage.Azure/Api.Storage.Azure/appsettings.json delete mode 100644 Api.Storage.Azure/Dockerfile delete mode 100644 Api.Storage.Azure/LICENSE delete mode 100644 Api.Storage.Azure/README.md delete mode 100644 Api.Storage.Azure/icon.png delete mode 100644 Api.Storage.Local/.docker/docker-compose.dcproj delete mode 100644 Api.Storage.Local/.docker/docker-compose.yml delete mode 100644 Api.Storage.Local/.dockerignore delete mode 100644 Api.Storage.Local/.github/config/slack.yml delete mode 100644 Api.Storage.Local/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Storage.Local/.gitignore delete mode 100644 Api.Storage.Local/.kubernetes/autoscaler.yaml delete mode 100644 Api.Storage.Local/.kubernetes/configmap.yaml delete mode 100644 Api.Storage.Local/.kubernetes/deployment.yaml delete mode 100644 Api.Storage.Local/.kubernetes/service.yaml delete mode 100644 Api.Storage.Local/.kubernetes/storage-pvc.yaml delete mode 100644 Api.Storage.Local/.kubernetes/storage-storageclass.yaml delete mode 100644 Api.Storage.Local/.tests/Tests.Api.Storage.Local/Properties/DoNotParallelize.cs delete mode 100644 Api.Storage.Local/.tests/Tests.Api.Storage.Local/Tests.Api.Storage.Local.csproj delete mode 100644 Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj delete mode 100644 Api.Storage.Local/Api.Storage.Local.sln delete mode 100644 Api.Storage.Local/Api.Storage.Local/Api.Storage.Local.csproj delete mode 100644 Api.Storage.Local/Api.Storage.Local/Controllers/ExamplesController.cs delete mode 100644 Api.Storage.Local/Api.Storage.Local/Dockerfile.Local delete mode 100644 Api.Storage.Local/Api.Storage.Local/Program.cs delete mode 100644 Api.Storage.Local/Api.Storage.Local/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Storage.Local/Api.Storage.Local/appsettings.Development.json delete mode 100644 Api.Storage.Local/Api.Storage.Local/appsettings.Production.json delete mode 100644 Api.Storage.Local/Api.Storage.Local/appsettings.Staging.json delete mode 100644 Api.Storage.Local/Api.Storage.Local/appsettings.json delete mode 100644 Api.Storage.Local/Dockerfile delete mode 100644 Api.Storage.Local/LICENSE delete mode 100644 Api.Storage.Local/README.md delete mode 100644 Api.Storage.Local/icon.png delete mode 100644 Api.TimeZone/.docker/docker-compose.dcproj delete mode 100644 Api.TimeZone/.docker/docker-compose.yml delete mode 100644 Api.TimeZone/.dockerignore delete mode 100644 Api.TimeZone/.github/config/slack.yml delete mode 100644 Api.TimeZone/.github/workflows/build-and-deploy.yml delete mode 100644 Api.TimeZone/.gitignore delete mode 100644 Api.TimeZone/.kubernetes/autoscaler.yaml delete mode 100644 Api.TimeZone/.kubernetes/configmap.yaml delete mode 100644 Api.TimeZone/.kubernetes/deployment.yaml delete mode 100644 Api.TimeZone/.kubernetes/service.yaml delete mode 100644 Api.TimeZone/.tests/Tests.Api.TimeZone/Properties/DoNotParallelize.cs delete mode 100644 Api.TimeZone/.tests/Tests.Api.TimeZone/Tests.Api.TimeZone.csproj delete mode 100644 Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj delete mode 100644 Api.TimeZone/Api.TimeZone.sln delete mode 100644 Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj delete mode 100644 Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs delete mode 100644 Api.TimeZone/Api.TimeZone/Controllers/Requests/DateTimeRequest.cs delete mode 100644 Api.TimeZone/Api.TimeZone/Dockerfile.Local delete mode 100644 Api.TimeZone/Api.TimeZone/Program.cs delete mode 100644 Api.TimeZone/Api.TimeZone/Properties/InternalsVisibleTo.cs delete mode 100644 Api.TimeZone/Api.TimeZone/appsettings.Development.json delete mode 100644 Api.TimeZone/Api.TimeZone/appsettings.Production.json delete mode 100644 Api.TimeZone/Api.TimeZone/appsettings.Staging.json delete mode 100644 Api.TimeZone/Api.TimeZone/appsettings.json delete mode 100644 Api.TimeZone/Dockerfile delete mode 100644 Api.TimeZone/LICENSE delete mode 100644 Api.TimeZone/README.md delete mode 100644 Api.TimeZone/icon.png delete mode 100644 Api.Versioning/.docker/docker-compose.dcproj delete mode 100644 Api.Versioning/.docker/docker-compose.yml delete mode 100644 Api.Versioning/.dockerignore delete mode 100644 Api.Versioning/.github/config/slack.yml delete mode 100644 Api.Versioning/.github/workflows/build-and-deploy.yml delete mode 100644 Api.Versioning/.gitignore delete mode 100644 Api.Versioning/.kubernetes/autoscaler.yaml delete mode 100644 Api.Versioning/.kubernetes/configmap.yaml delete mode 100644 Api.Versioning/.kubernetes/deployment.yaml delete mode 100644 Api.Versioning/.kubernetes/service.yaml delete mode 100644 Api.Versioning/.tests/Tests.Api.Versioning/Properties/DoNotParallelize.cs delete mode 100644 Api.Versioning/.tests/Tests.Api.Versioning/Tests.Api.Versioning.csproj delete mode 100644 Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj delete mode 100644 Api.Versioning/Api.Versioning.sln delete mode 100644 Api.Versioning/Api.Versioning/Api.Versioning.csproj delete mode 100644 Api.Versioning/Api.Versioning/Controllers/ExamplesController.cs delete mode 100644 Api.Versioning/Api.Versioning/Dockerfile.Local delete mode 100644 Api.Versioning/Api.Versioning/Program.cs delete mode 100644 Api.Versioning/Api.Versioning/Properties/InternalsVisibleTo.cs delete mode 100644 Api.Versioning/Api.Versioning/appsettings.Development.json delete mode 100644 Api.Versioning/Api.Versioning/appsettings.Production.json delete mode 100644 Api.Versioning/Api.Versioning/appsettings.Staging.json delete mode 100644 Api.Versioning/Api.Versioning/appsettings.json delete mode 100644 Api.Versioning/Dockerfile delete mode 100644 Api.Versioning/LICENSE delete mode 100644 Api.Versioning/README.md delete mode 100644 Api.Versioning/icon.png delete mode 100644 Api.VirusScan/.docker/docker-compose.dcproj delete mode 100644 Api.VirusScan/.docker/docker-compose.yml delete mode 100644 Api.VirusScan/.dockerignore delete mode 100644 Api.VirusScan/.github/config/slack.yml delete mode 100644 Api.VirusScan/.github/workflows/build-and-deploy.yml delete mode 100644 Api.VirusScan/.gitignore delete mode 100644 Api.VirusScan/.kubernetes/autoscaler.yaml delete mode 100644 Api.VirusScan/.kubernetes/configmap.yaml delete mode 100644 Api.VirusScan/.kubernetes/deployment.yaml delete mode 100644 Api.VirusScan/.kubernetes/service.yaml delete mode 100644 Api.VirusScan/.tests/Tests.Api.VirusScan/Properties/DoNotParallelize.cs delete mode 100644 Api.VirusScan/.tests/Tests.Api.VirusScan/Tests.Api.VirusScan.csproj delete mode 100644 Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj delete mode 100644 Api.VirusScan/Api.VirusScan.sln delete mode 100644 Api.VirusScan/Api.VirusScan/Api.VirusScan.csproj delete mode 100644 Api.VirusScan/Api.VirusScan/Controllers/ExamplesController.cs delete mode 100644 Api.VirusScan/Api.VirusScan/Dockerfile.Local delete mode 100644 Api.VirusScan/Api.VirusScan/Program.cs delete mode 100644 Api.VirusScan/Api.VirusScan/Properties/InternalsVisibleTo.cs delete mode 100644 Api.VirusScan/Api.VirusScan/appsettings.Development.json delete mode 100644 Api.VirusScan/Api.VirusScan/appsettings.Production.json delete mode 100644 Api.VirusScan/Api.VirusScan/appsettings.Staging.json delete mode 100644 Api.VirusScan/Api.VirusScan/appsettings.json delete mode 100644 Api.VirusScan/Dockerfile delete mode 100644 Api.VirusScan/LICENSE delete mode 100644 Api.VirusScan/README.md delete mode 100644 Api.VirusScan/icon.png delete mode 100644 Api._Blank/.docker/docker-compose.dcproj delete mode 100644 Api._Blank/.docker/docker-compose.yml delete mode 100644 Api._Blank/.dockerignore delete mode 100644 Api._Blank/.github/config/slack.yml delete mode 100644 Api._Blank/.github/workflows/build-and-deploy.yml delete mode 100644 Api._Blank/.gitignore delete mode 100644 Api._Blank/.kubernetes/autoscaler.yaml delete mode 100644 Api._Blank/.kubernetes/configmap.yaml delete mode 100644 Api._Blank/.kubernetes/deployment.yaml delete mode 100644 Api._Blank/.kubernetes/service.yaml delete mode 100644 Api._Blank/.tests/Tests.Api.Blank/Properties/DoNotParallelize.cs delete mode 100644 Api._Blank/.tests/Tests.Api.Blank/Tests.Api.Blank.csproj delete mode 100644 Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj delete mode 100644 Api._Blank/Api.Blank.sln delete mode 100644 Api._Blank/Api.Blank/Api.Blank.csproj delete mode 100644 Api._Blank/Api.Blank/Dockerfile.Local delete mode 100644 Api._Blank/Api.Blank/Program.cs delete mode 100644 Api._Blank/Api.Blank/Properties/InternalsVisibleTo.cs delete mode 100644 Api._Blank/Api.Blank/appsettings.Development.json delete mode 100644 Api._Blank/Api.Blank/appsettings.Production.json delete mode 100644 Api._Blank/Api.Blank/appsettings.Staging.json delete mode 100644 Api._Blank/Api.Blank/appsettings.json delete mode 100644 Api._Blank/Dockerfile delete mode 100644 Api._Blank/LICENSE delete mode 100644 Api._Blank/README.md delete mode 100644 Api._Blank/icon.png delete mode 100644 Console.CustomConfigSection/.docker/docker-compose.dcproj delete mode 100644 Console.CustomConfigSection/.docker/docker-compose.yml delete mode 100644 Console.CustomConfigSection/.dockerignore delete mode 100644 Console.CustomConfigSection/.github/config/slack.yml delete mode 100644 Console.CustomConfigSection/.github/workflows/build-and-deploy.yml delete mode 100644 Console.CustomConfigSection/.gitignore delete mode 100644 Console.CustomConfigSection/.kubernetes/configmap.yaml delete mode 100644 Console.CustomConfigSection/.kubernetes/cronjob.yaml delete mode 100644 Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Properties/DoNotParallelize.cs delete mode 100644 Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Tests.Console.CustomConfigSection.csproj delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection.sln delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Config/CustomOptions.cs delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Console.CustomConfigSection.csproj delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Dockerfile.Local delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Program.cs delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Properties/InternalsVisibleTo.cs delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Workers/ExampleWorker.cs delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Development.json delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Production.json delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Staging.json delete mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/appsettings.json delete mode 100644 Console.CustomConfigSection/Dockerfile delete mode 100644 Console.CustomConfigSection/README.md delete mode 100644 Console.CustomService/.docker/docker-compose.dcproj delete mode 100644 Console.CustomService/.docker/docker-compose.yml delete mode 100644 Console.CustomService/.dockerignore delete mode 100644 Console.CustomService/.github/config/slack.yml delete mode 100644 Console.CustomService/.github/workflows/build-and-deploy.yml delete mode 100644 Console.CustomService/.gitignore delete mode 100644 Console.CustomService/.kubernetes/configmap.yaml delete mode 100644 Console.CustomService/.kubernetes/cronjob.yaml delete mode 100644 Console.CustomService/.tests/Tests.Console.CustomService/Properties/DoNotParallelize.cs delete mode 100644 Console.CustomService/.tests/Tests.Console.CustomService/Tests.Console.CustomService.csproj delete mode 100644 Console.CustomService/Console.CustomService.sln delete mode 100644 Console.CustomService/Console.CustomService/Console.CustomService.csproj delete mode 100644 Console.CustomService/Console.CustomService/Dockerfile.Local delete mode 100644 Console.CustomService/Console.CustomService/Program.cs delete mode 100644 Console.CustomService/Console.CustomService/Properties/InternalsVisibleTo.cs delete mode 100644 Console.CustomService/Console.CustomService/Services/Abstractions/IExampleService.cs delete mode 100644 Console.CustomService/Console.CustomService/Services/ExampleService.cs delete mode 100644 Console.CustomService/Console.CustomService/Workers/ExampleWorker.cs delete mode 100644 Console.CustomService/Console.CustomService/appsettings.Development.json delete mode 100644 Console.CustomService/Console.CustomService/appsettings.Production.json delete mode 100644 Console.CustomService/Console.CustomService/appsettings.Staging.json delete mode 100644 Console.CustomService/Console.CustomService/appsettings.json delete mode 100644 Console.CustomService/Dockerfile delete mode 100644 Console.CustomService/README.md delete mode 100644 Console.Data.InMemory/.docker/docker-compose.dcproj delete mode 100644 Console.Data.InMemory/.docker/docker-compose.yml delete mode 100644 Console.Data.InMemory/.dockerignore delete mode 100644 Console.Data.InMemory/.github/config/slack.yml delete mode 100644 Console.Data.InMemory/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Data.InMemory/.gitignore delete mode 100644 Console.Data.InMemory/.kubernetes/configmap.yaml delete mode 100644 Console.Data.InMemory/.kubernetes/cronjob.yaml delete mode 100644 Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Properties/DoNotParallelize.cs delete mode 100644 Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Tests.Console.Data.InMemory.csproj delete mode 100644 Console.Data.InMemory/Console.Data.InMemory.sln delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/Console.Data.InMemory.csproj delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/Data/InMemoryDbContext.cs delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/Data/Mappings/ExampleMapping.cs delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/Data/Models/Example.cs delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/Dockerfile.Local delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/Program.cs delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/Workers/ExampleWorker.cs delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/appsettings.Development.json delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/appsettings.Production.json delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/appsettings.Staging.json delete mode 100644 Console.Data.InMemory/Console.Data.InMemory/appsettings.json delete mode 100644 Console.Data.InMemory/Dockerfile delete mode 100644 Console.Data.InMemory/README.md delete mode 100644 Console.Data.MySql/.docker/docker-compose.dcproj delete mode 100644 Console.Data.MySql/.docker/docker-compose.yml delete mode 100644 Console.Data.MySql/.dockerignore delete mode 100644 Console.Data.MySql/.github/config/slack.yml delete mode 100644 Console.Data.MySql/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Data.MySql/.gitignore delete mode 100644 Console.Data.MySql/.kubernetes/configmap.yaml delete mode 100644 Console.Data.MySql/.kubernetes/cronjob.yaml delete mode 100644 Console.Data.MySql/.tests/Tests.Console.Data.MySql/Properties/DoNotParallelize.cs delete mode 100644 Console.Data.MySql/.tests/Tests.Console.Data.MySql/Tests.Console.Data.MySql.csproj delete mode 100644 Console.Data.MySql/Console.Data.MySql.sln delete mode 100644 Console.Data.MySql/Console.Data.MySql/Console.Data.MySql.csproj delete mode 100644 Console.Data.MySql/Console.Data.MySql/Data/Mappings/ExampleMapping.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Data/Models/Example.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContext.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContextFactory.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Dockerfile.Local delete mode 100644 Console.Data.MySql/Console.Data.MySql/Migrations/20260311110547_Initial.Designer.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Migrations/20260311110547_Initial.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Program.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/Workers/ExampleWorker.cs delete mode 100644 Console.Data.MySql/Console.Data.MySql/appsettings.Development.json delete mode 100644 Console.Data.MySql/Console.Data.MySql/appsettings.Production.json delete mode 100644 Console.Data.MySql/Console.Data.MySql/appsettings.Staging.json delete mode 100644 Console.Data.MySql/Console.Data.MySql/appsettings.json delete mode 100644 Console.Data.MySql/Dockerfile delete mode 100644 Console.Data.MySql/README.md delete mode 100644 Console.Data.PostgreSQL/.docker/docker-compose.dcproj delete mode 100644 Console.Data.PostgreSQL/.docker/docker-compose.yml delete mode 100644 Console.Data.PostgreSQL/.dockerignore delete mode 100644 Console.Data.PostgreSQL/.github/config/slack.yml delete mode 100644 Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Data.PostgreSQL/.gitignore delete mode 100644 Console.Data.PostgreSQL/.kubernetes/configmap.yaml delete mode 100644 Console.Data.PostgreSQL/.kubernetes/cronjob.yaml delete mode 100644 Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Properties/DoNotParallelize.cs delete mode 100644 Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Tests.Console.Data.PostgreSQL.csproj delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL.sln delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Console.Data.PostgreSQL.csproj delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Models/Example.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContext.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Dockerfile.Local delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260311120716_Initial.Designer.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260311120716_Initial.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Workers/ExampleWorker.cs delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Development.json delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Production.json delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Staging.json delete mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.json delete mode 100644 Console.Data.PostgreSQL/Dockerfile delete mode 100644 Console.Data.PostgreSQL/README.md delete mode 100644 Console.Data.SqLite/.docker/docker-compose.dcproj delete mode 100644 Console.Data.SqLite/.docker/docker-compose.yml delete mode 100644 Console.Data.SqLite/.dockerignore delete mode 100644 Console.Data.SqLite/.github/config/slack.yml delete mode 100644 Console.Data.SqLite/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Data.SqLite/.gitignore delete mode 100644 Console.Data.SqLite/.kubernetes/configmap.yaml delete mode 100644 Console.Data.SqLite/.kubernetes/cronjob.yaml delete mode 100644 Console.Data.SqLite/.kubernetes/data-pvc.yaml delete mode 100644 Console.Data.SqLite/.kubernetes/data-storageclass.yaml delete mode 100644 Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Properties/DoNotParallelize.cs delete mode 100644 Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Tests.Console.Data.SqLite.csproj delete mode 100644 Console.Data.SqLite/Console.Data.SqLite.sln delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Console.Data.SqLite.csproj delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Data/Mappings/ExampleMapping.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Data/Models/Example.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContext.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContextFactory.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Dockerfile.Local delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Migrations/20260311131050_Initial.Designer.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Migrations/20260311131050_Initial.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Program.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/Workers/ExampleWorker.cs delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/appsettings.Production.json delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/appsettings.Staging.json delete mode 100644 Console.Data.SqLite/Console.Data.SqLite/appsettings.json delete mode 100644 Console.Data.SqLite/Dockerfile delete mode 100644 Console.Data.SqLite/README.md delete mode 100644 Console.Data.SqlServer/.docker/docker-compose.dcproj delete mode 100644 Console.Data.SqlServer/.docker/docker-compose.yml delete mode 100644 Console.Data.SqlServer/.dockerignore delete mode 100644 Console.Data.SqlServer/.github/config/slack.yml delete mode 100644 Console.Data.SqlServer/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Data.SqlServer/.gitignore delete mode 100644 Console.Data.SqlServer/.kubernetes/configmap.yaml delete mode 100644 Console.Data.SqlServer/.kubernetes/cronjob.yaml delete mode 100644 Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Properties/DoNotParallelize.cs delete mode 100644 Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Tests.Console.Data.SqlServer.csproj delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer.sln delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Console.Data.SqlServer.csproj delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Data/Mappings/ExampleMapping.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Data/Models/Example.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContext.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContextFactory.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Dockerfile.Local delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260311103638_Initial.Designer.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260311103638_Initial.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Program.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Workers/ExampleWorker.cs delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Development.json delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Production.json delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Staging.json delete mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/appsettings.json delete mode 100644 Console.Data.SqlServer/Dockerfile delete mode 100644 Console.Data.SqlServer/README.md delete mode 100644 Console.Eventing.RabbitMq/.docker/docker-compose.dcproj delete mode 100644 Console.Eventing.RabbitMq/.docker/docker-compose.yml delete mode 100644 Console.Eventing.RabbitMq/.dockerignore delete mode 100644 Console.Eventing.RabbitMq/.github/config/slack.yml delete mode 100644 Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Eventing.RabbitMq/.gitignore delete mode 100644 Console.Eventing.RabbitMq/.kubernetes/configmap.yaml delete mode 100644 Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml delete mode 100644 Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Properties/DoNotParallelize.cs delete mode 100644 Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Tests.Console.Eventing.RabbitMq.csproj delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.sln delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.csproj delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Dockerfile.Local delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/EventingHandler.cs delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/Models/EventModel.cs delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Program.cs delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Workers/ExampleWorker.cs delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Development.json delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Production.json delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Staging.json delete mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.json delete mode 100644 Console.Eventing.RabbitMq/Dockerfile delete mode 100644 Console.Eventing.RabbitMq/README.md delete mode 100644 Console.ExceptionHandling/.docker/docker-compose.dcproj delete mode 100644 Console.ExceptionHandling/.docker/docker-compose.yml delete mode 100644 Console.ExceptionHandling/.dockerignore delete mode 100644 Console.ExceptionHandling/.github/config/slack.yml delete mode 100644 Console.ExceptionHandling/.github/workflows/build-and-deploy.yml delete mode 100644 Console.ExceptionHandling/.gitignore delete mode 100644 Console.ExceptionHandling/.kubernetes/configmap.yaml delete mode 100644 Console.ExceptionHandling/.kubernetes/cronjob.yaml delete mode 100644 Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Properties/DoNotParallelize.cs delete mode 100644 Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Tests.Console.ExceptionHandling.csproj delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling.sln delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Console.ExceptionHandling.csproj delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Dockerfile.Local delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Program.cs delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Properties/InternalsVisibleTo.cs delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Workers/ExampleWorker.cs delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Development.json delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Production.json delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Staging.json delete mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/appsettings.json delete mode 100644 Console.ExceptionHandling/Dockerfile delete mode 100644 Console.ExceptionHandling/README.md delete mode 100644 Console.Localization/.docker/docker-compose.dcproj delete mode 100644 Console.Localization/.docker/docker-compose.yml delete mode 100644 Console.Localization/.dockerignore delete mode 100644 Console.Localization/.github/config/slack.yml delete mode 100644 Console.Localization/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Localization/.gitignore delete mode 100644 Console.Localization/.kubernetes/configmap.yaml delete mode 100644 Console.Localization/.kubernetes/cronjob.yaml delete mode 100644 Console.Localization/.tests/Tests.Console.Localization/Properties/DoNotParallelize.cs delete mode 100644 Console.Localization/.tests/Tests.Console.Localization/Tests.Console.Localization.csproj delete mode 100644 Console.Localization/Console.Localization.sln delete mode 100644 Console.Localization/Console.Localization/Console.Localization.csproj delete mode 100644 Console.Localization/Console.Localization/Dockerfile.Local delete mode 100644 Console.Localization/Console.Localization/Program.cs delete mode 100644 Console.Localization/Console.Localization/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Localization/Console.Localization/Workers/ExampleWorker.cs delete mode 100644 Console.Localization/Console.Localization/appsettings.Development.json delete mode 100644 Console.Localization/Console.Localization/appsettings.Production.json delete mode 100644 Console.Localization/Console.Localization/appsettings.Staging.json delete mode 100644 Console.Localization/Console.Localization/appsettings.json delete mode 100644 Console.Localization/Dockerfile delete mode 100644 Console.Localization/README.md delete mode 100644 Console.Logging.Log4Net/.docker/docker-compose.dcproj delete mode 100644 Console.Logging.Log4Net/.docker/docker-compose.yml delete mode 100644 Console.Logging.Log4Net/.dockerignore delete mode 100644 Console.Logging.Log4Net/.github/config/slack.yml delete mode 100644 Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Logging.Log4Net/.gitignore delete mode 100644 Console.Logging.Log4Net/.kubernetes/configmap.yaml delete mode 100644 Console.Logging.Log4Net/.kubernetes/cronjob.yaml delete mode 100644 Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Properties/DoNotParallelize.cs delete mode 100644 Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Tests.Console.Logging.Log4Net.csproj delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net.sln delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Console.Logging.Log4Net.csproj delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Dockerfile.Local delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Program.cs delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Workers/ExampleWorker.cs delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Development.json delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Production.json delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Staging.json delete mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.json delete mode 100644 Console.Logging.Log4Net/Dockerfile delete mode 100644 Console.Logging.Log4Net/README.md delete mode 100644 Console.Logging.Microsoft/.docker/docker-compose.dcproj delete mode 100644 Console.Logging.Microsoft/.docker/docker-compose.yml delete mode 100644 Console.Logging.Microsoft/.dockerignore delete mode 100644 Console.Logging.Microsoft/.github/config/slack.yml delete mode 100644 Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Logging.Microsoft/.gitignore delete mode 100644 Console.Logging.Microsoft/.kubernetes/configmap.yaml delete mode 100644 Console.Logging.Microsoft/.kubernetes/cronjob.yaml delete mode 100644 Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Properties/DoNotParallelize.cs delete mode 100644 Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Tests.Console.Logging.Microsoft.csproj delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft.sln delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Console.Logging.Microsoft.csproj delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Dockerfile.Local delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Program.cs delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Workers/ExampleWorker.cs delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Development.json delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Production.json delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Staging.json delete mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.json delete mode 100644 Console.Logging.Microsoft/Dockerfile delete mode 100644 Console.Logging.Microsoft/README.md delete mode 100644 Console.Logging.NLog/.docker/docker-compose.dcproj delete mode 100644 Console.Logging.NLog/.docker/docker-compose.yml delete mode 100644 Console.Logging.NLog/.dockerignore delete mode 100644 Console.Logging.NLog/.github/config/slack.yml delete mode 100644 Console.Logging.NLog/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Logging.NLog/.gitignore delete mode 100644 Console.Logging.NLog/.kubernetes/configmap.yaml delete mode 100644 Console.Logging.NLog/.kubernetes/cronjob.yaml delete mode 100644 Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Properties/DoNotParallelize.cs delete mode 100644 Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Tests.Console.Logging.NLog.csproj delete mode 100644 Console.Logging.NLog/Console.Logging.NLog.sln delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/Console.Logging.NLog.csproj delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/Dockerfile.Local delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/Program.cs delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/Workers/ExampleWorker.cs delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/appsettings.Development.json delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/appsettings.Production.json delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/appsettings.Staging.json delete mode 100644 Console.Logging.NLog/Console.Logging.NLog/appsettings.json delete mode 100644 Console.Logging.NLog/Dockerfile delete mode 100644 Console.Logging.NLog/README.md delete mode 100644 Console.Logging.Serilog/.docker/docker-compose.dcproj delete mode 100644 Console.Logging.Serilog/.docker/docker-compose.yml delete mode 100644 Console.Logging.Serilog/.dockerignore delete mode 100644 Console.Logging.Serilog/.github/config/slack.yml delete mode 100644 Console.Logging.Serilog/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Logging.Serilog/.gitignore delete mode 100644 Console.Logging.Serilog/.kubernetes/configmap.yaml delete mode 100644 Console.Logging.Serilog/.kubernetes/cronjob.yaml delete mode 100644 Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Properties/DoNotParallelize.cs delete mode 100644 Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Tests.Console.Logging.Serilog.csproj delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog.sln delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Console.Logging.Serilog.csproj delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Dockerfile.Local delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Program.cs delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Workers/ExampleWorker.cs delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Development.json delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Production.json delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Staging.json delete mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/appsettings.json delete mode 100644 Console.Logging.Serilog/Dockerfile delete mode 100644 Console.Logging.Serilog/README.md delete mode 100644 Console.StartupTasks/.docker/docker-compose.dcproj delete mode 100644 Console.StartupTasks/.docker/docker-compose.yml delete mode 100644 Console.StartupTasks/.dockerignore delete mode 100644 Console.StartupTasks/.github/config/slack.yml delete mode 100644 Console.StartupTasks/.github/workflows/build-and-deploy.yml delete mode 100644 Console.StartupTasks/.gitignore delete mode 100644 Console.StartupTasks/.kubernetes/configmap.yaml delete mode 100644 Console.StartupTasks/.kubernetes/cronjob.yaml delete mode 100644 Console.StartupTasks/.tests/Tests.Console.StartupTasks/Properties/DoNotParallelize.cs delete mode 100644 Console.StartupTasks/.tests/Tests.Console.StartupTasks/Tests.Console.StartupTasks.csproj delete mode 100644 Console.StartupTasks/Console.StartupTasks.sln delete mode 100644 Console.StartupTasks/Console.StartupTasks/Console.StartupTasks.csproj delete mode 100644 Console.StartupTasks/Console.StartupTasks/Dockerfile.Local delete mode 100644 Console.StartupTasks/Console.StartupTasks/Program.cs delete mode 100644 Console.StartupTasks/Console.StartupTasks/Properties/InternalsVisibleTo.cs delete mode 100644 Console.StartupTasks/Console.StartupTasks/Startup/ExampleStartupTask.cs delete mode 100644 Console.StartupTasks/Console.StartupTasks/Workers/ExampleWorker.cs delete mode 100644 Console.StartupTasks/Console.StartupTasks/appsettings.Development.json delete mode 100644 Console.StartupTasks/Console.StartupTasks/appsettings.Production.json delete mode 100644 Console.StartupTasks/Console.StartupTasks/appsettings.Staging.json delete mode 100644 Console.StartupTasks/Console.StartupTasks/appsettings.json delete mode 100644 Console.StartupTasks/Dockerfile delete mode 100644 Console.StartupTasks/README.md delete mode 100644 Console.Storage.Azure/.docker/docker-compose.dcproj delete mode 100644 Console.Storage.Azure/.docker/docker-compose.yml delete mode 100644 Console.Storage.Azure/.dockerignore delete mode 100644 Console.Storage.Azure/.github/config/slack.yml delete mode 100644 Console.Storage.Azure/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Storage.Azure/.gitignore delete mode 100644 Console.Storage.Azure/.kubernetes/configmap.yaml delete mode 100644 Console.Storage.Azure/.kubernetes/cronjob.yaml delete mode 100644 Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Properties/DoNotParallelize.cs delete mode 100644 Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Tests.Console.Storage.Azure.csproj delete mode 100644 Console.Storage.Azure/Console.Storage.Azure.sln delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/Console.Storage.Azure.csproj delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/Dockerfile.Local delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/Program.cs delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/Workers/ExampleWorker.cs delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/appsettings.Development.json delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/appsettings.Production.json delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/appsettings.Staging.json delete mode 100644 Console.Storage.Azure/Console.Storage.Azure/appsettings.json delete mode 100644 Console.Storage.Azure/Dockerfile delete mode 100644 Console.Storage.Azure/README.md delete mode 100644 Console.Storage.Local/.docker/docker-compose.dcproj delete mode 100644 Console.Storage.Local/.docker/docker-compose.yml delete mode 100644 Console.Storage.Local/.dockerignore delete mode 100644 Console.Storage.Local/.github/config/slack.yml delete mode 100644 Console.Storage.Local/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Storage.Local/.gitignore delete mode 100644 Console.Storage.Local/.kubernetes/configmap.yaml delete mode 100644 Console.Storage.Local/.kubernetes/cronjob.yaml delete mode 100644 Console.Storage.Local/.kubernetes/storage-pvc.yaml delete mode 100644 Console.Storage.Local/.kubernetes/storage-storageclass.yaml delete mode 100644 Console.Storage.Local/.tests/Tests.Console.Storage.Local/Properties/DoNotParallelize.cs delete mode 100644 Console.Storage.Local/.tests/Tests.Console.Storage.Local/Tests.Console.Storage.Local.csproj delete mode 100644 Console.Storage.Local/Console.Storage.Local.sln delete mode 100644 Console.Storage.Local/Console.Storage.Local/Console.Storage.Local.csproj delete mode 100644 Console.Storage.Local/Console.Storage.Local/Dockerfile.Local delete mode 100644 Console.Storage.Local/Console.Storage.Local/Program.cs delete mode 100644 Console.Storage.Local/Console.Storage.Local/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Storage.Local/Console.Storage.Local/Workers/ExampleWorker.cs delete mode 100644 Console.Storage.Local/Console.Storage.Local/appsettings.Development.json delete mode 100644 Console.Storage.Local/Console.Storage.Local/appsettings.Production.json delete mode 100644 Console.Storage.Local/Console.Storage.Local/appsettings.Staging.json delete mode 100644 Console.Storage.Local/Console.Storage.Local/appsettings.json delete mode 100644 Console.Storage.Local/Dockerfile delete mode 100644 Console.Storage.Local/README.md delete mode 100644 Console.Workers/.docker/docker-compose.dcproj delete mode 100644 Console.Workers/.docker/docker-compose.yml delete mode 100644 Console.Workers/.dockerignore delete mode 100644 Console.Workers/.github/config/slack.yml delete mode 100644 Console.Workers/.github/workflows/build-and-deploy.yml delete mode 100644 Console.Workers/.gitignore delete mode 100644 Console.Workers/.kubernetes/configmap.yaml delete mode 100644 Console.Workers/.kubernetes/cronjob.yaml delete mode 100644 Console.Workers/.tests/Tests.Console.Workers/Properties/DoNotParallelize.cs delete mode 100644 Console.Workers/.tests/Tests.Console.Workers/Tests.Console.Workers.csproj delete mode 100644 Console.Workers/Console.Workers.sln delete mode 100644 Console.Workers/Console.Workers/Console.Workers.csproj delete mode 100644 Console.Workers/Console.Workers/Dockerfile.Local delete mode 100644 Console.Workers/Console.Workers/Program.cs delete mode 100644 Console.Workers/Console.Workers/Properties/InternalsVisibleTo.cs delete mode 100644 Console.Workers/Console.Workers/Workers/AnotherExampleWorker.cs delete mode 100644 Console.Workers/Console.Workers/Workers/ExampleWorker.cs delete mode 100644 Console.Workers/Console.Workers/appsettings.Development.json delete mode 100644 Console.Workers/Console.Workers/appsettings.Production.json delete mode 100644 Console.Workers/Console.Workers/appsettings.Staging.json delete mode 100644 Console.Workers/Console.Workers/appsettings.json delete mode 100644 Console.Workers/Dockerfile delete mode 100644 Console.Workers/README.md delete mode 100644 Console._Blank/.docker/docker-compose.dcproj delete mode 100644 Console._Blank/.docker/docker-compose.yml delete mode 100644 Console._Blank/.dockerignore delete mode 100644 Console._Blank/.github/config/slack.yml delete mode 100644 Console._Blank/.github/workflows/build-and-deploy.yml delete mode 100644 Console._Blank/.gitignore delete mode 100644 Console._Blank/.kubernetes/configmap.yaml delete mode 100644 Console._Blank/.kubernetes/cronjob.yaml delete mode 100644 Console._Blank/.tests/Tests.Console.Blank/Properties/DoNotParallelize.cs delete mode 100644 Console._Blank/.tests/Tests.Console.Blank/Tests.Console.Blank.csproj delete mode 100644 Console._Blank/Console.Blank.sln delete mode 100644 Console._Blank/Console.Blank/Console.Blank.csproj delete mode 100644 Console._Blank/Console.Blank/Dockerfile.Local delete mode 100644 Console._Blank/Console.Blank/Program.cs delete mode 100644 Console._Blank/Console.Blank/Properties/InternalsVisibleTo.cs delete mode 100644 Console._Blank/Console.Blank/appsettings.Development.json delete mode 100644 Console._Blank/Console.Blank/appsettings.Production.json delete mode 100644 Console._Blank/Console.Blank/appsettings.Staging.json delete mode 100644 Console._Blank/Console.Blank/appsettings.json delete mode 100644 Console._Blank/Dockerfile delete mode 100644 Console._Blank/README.md delete mode 100644 Web._Blank/.docker/docker-compose.dcproj delete mode 100644 Web._Blank/.docker/docker-compose.yml delete mode 100644 Web._Blank/.dockerignore delete mode 100644 Web._Blank/.github/config/slack.yml delete mode 100644 Web._Blank/.github/workflows/build-and-deploy.yml delete mode 100644 Web._Blank/.gitignore delete mode 100644 Web._Blank/.kubernetes/autoscaler.yaml delete mode 100644 Web._Blank/.kubernetes/configmap.yaml delete mode 100644 Web._Blank/.kubernetes/deployment.yaml delete mode 100644 Web._Blank/.kubernetes/service.yaml delete mode 100644 Web._Blank/.tests/Tests.Web.Blank/Properties/DoNotParallelize.cs delete mode 100644 Web._Blank/.tests/Tests.Web.Blank/Tests.Web.Blank.csproj delete mode 100644 Web._Blank/Dockerfile delete mode 100644 Web._Blank/LICENSE delete mode 100644 Web._Blank/README.md delete mode 100644 Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj delete mode 100644 Web._Blank/Web.Blank.sln delete mode 100644 Web._Blank/Web.Blank/App.razor delete mode 100644 Web._Blank/Web.Blank/Dockerfile.Local delete mode 100644 Web._Blank/Web.Blank/Program.cs delete mode 100644 Web._Blank/Web.Blank/Properties/InternalsVisibleTo.cs delete mode 100644 Web._Blank/Web.Blank/Web.Blank.csproj delete mode 100644 Web._Blank/Web.Blank/appsettings.Development.json delete mode 100644 Web._Blank/Web.Blank/appsettings.Production.json delete mode 100644 Web._Blank/Web.Blank/appsettings.Staging.json delete mode 100644 Web._Blank/Web.Blank/appsettings.json delete mode 100644 Web._Blank/icon.png diff --git a/Api.ApiClients.Entity/.docker/docker-compose.dcproj b/Api.ApiClients.Entity/.docker/docker-compose.dcproj deleted file mode 100644 index d9e8e500..00000000 --- a/Api.ApiClients.Entity/.docker/docker-compose.dcproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.ApiClients.Entity/.docker/docker-compose.yml b/Api.ApiClients.Entity/.docker/docker-compose.yml deleted file mode 100644 index c93a17e7..00000000 --- a/Api.ApiClients.Entity/.docker/docker-compose.yml +++ /dev/null @@ -1,44 +0,0 @@ -services: - api.apiclients.entity: - image: api.apiclients.entity - hostname: api.apiclients.entity - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.ApiClients.Entity - dockerfile: "Dockerfile.Local" - networks: - - network - - api.apiclients.entity.service: - image: api.apiclients.entity.service - hostname: api-apiclients-entity-service - restart: on-failure - ports: - - 8181:8181 - build: - context: ../Api.ApiClients.Entity.Service - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.ApiClients.Entity/.dockerignore b/Api.ApiClients.Entity/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.ApiClients.Entity/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.ApiClients.Entity/.github/config/slack.yml b/Api.ApiClients.Entity/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.ApiClients.Entity/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml b/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 62afcad6..00000000 --- a/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.ApiClients.Entity - IMAGE_NAME: api.apiclients.entity - SERVICE_NAME: api-apiclients-entity - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ApiClients.Entity/.gitignore b/Api.ApiClients.Entity/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.ApiClients.Entity/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ApiClients.Entity/.kubernetes/autoscaler.yaml b/Api.ApiClients.Entity/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.ApiClients.Entity/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ApiClients.Entity/.kubernetes/configmap.yaml b/Api.ApiClients.Entity/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.ApiClients.Entity/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ApiClients.Entity/.kubernetes/deployment.yaml b/Api.ApiClients.Entity/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.ApiClients.Entity/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ApiClients.Entity/.kubernetes/service.yaml b/Api.ApiClients.Entity/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.ApiClients.Entity/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Properties/DoNotParallelize.cs b/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Tests.Api.ApiClients.Entity.csproj b/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Tests.Api.ApiClients.Entity.csproj deleted file mode 100644 index 1ef66e7c..00000000 --- a/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Tests.Api.ApiClients.Entity.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api/NanoApiClient.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api/NanoApiClient.cs deleted file mode 100644 index ab08eb1d..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api/NanoApiClient.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Nano.App.ApiClient; - -namespace Api.ApiClients.Entity.Service.Models.Api; - -/// -/// Nano Api Client. -/// -public class NanoApiClient(Nano.App.ApiClient.ApiClient apiClient) : BaseApiClient(apiClient); \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Criterias/ExampleQueryCriteria.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 93981b5c..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.ApiClients.Entity.Service.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Example.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Example.cs deleted file mode 100644 index 82066324..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.ApiClients.Entity.Service.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Api.ApiClients.Entity.Service.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Api.ApiClients.Entity.Service.csproj deleted file mode 100644 index db9ec4c1..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Api.ApiClients.Entity.Service.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Controllers/ExamplesController.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Controllers/ExamplesController.cs deleted file mode 100644 index 8ccd2e54..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.ApiClients.Entity.Service.Models; -using Api.ApiClients.Entity.Service.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.ApiClients.Entity.Service.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/Mappings/ExampleMapping.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 6a2ed7b4..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.ApiClients.Entity.Service.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.ApiClients.Entity.Service.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContext.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContext.cs deleted file mode 100644 index add58857..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.ApiClients.Entity.Service.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContextFactory.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContextFactory.cs deleted file mode 100644 index f811c214..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.ApiClients.Entity.Service.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local deleted file mode 100644 index 9223f500..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ApiClients.Entity.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.Designer.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.Designer.cs deleted file mode 100644 index 4859280c..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.Designer.cs +++ /dev/null @@ -1,651 +0,0 @@ -// -using System; -using Api.ApiClients.Entity.Service.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.ApiClient.Service.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260422105215_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.ApiClient.Service.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.cs deleted file mode 100644 index aa2ac68e..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.ApiClient.Service.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/MySqlDbContextModelSnapshot.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index c1e0cae7..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,648 +0,0 @@ -// -using System; -using Api.ApiClients.Entity.Service.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.ApiClient.Service.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.ApiClient.Service.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Program.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Program.cs deleted file mode 100644 index 78918468..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.ApiClients.Entity.Service.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Properties/InternalsVisibleTo.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 9a663cce..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ApiClients.Entity")] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Development.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Production.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Staging.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json deleted file mode 100644 index dbd68122..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8181 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.sln b/Api.ApiClients.Entity/Api.ApiClients.Entity.sln deleted file mode 100644 index 9d759bd7..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity.sln +++ /dev/null @@ -1,159 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Entity.Models", "Api.ApiClients.Entity.Models\Api.ApiClients.Entity.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Entity", "Api.ApiClients.Entity\Api.ApiClients.Entity.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ApiClients.Entity", ".tests\Tests.Api.ApiClients.Entity\Tests.Api.ApiClients.Entity.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Entity.Service.Models", "Api.ApiClients.Entity.Service.Models\Api.ApiClients.Entity.Service.Models.csproj", "{72C75C0B-EACD-A113-B9A4-2600519A71E0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Entity.Service", "Api.ApiClients.Entity.Service\Api.ApiClients.Entity.Service.csproj", "{37CEC825-166A-5BB7-9466-A75FFE32383F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.Build.0 = Release|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Api.ApiClients.Entity.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity/Api.ApiClients.Entity.csproj deleted file mode 100644 index 71b6269b..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/Api.ApiClients.Entity.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Controllers/ExamplesController.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity/Controllers/ExamplesController.cs deleted file mode 100644 index 3e211752..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/Controllers/ExamplesController.cs +++ /dev/null @@ -1,732 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.ApiClients.Entity.Service.Models; -using Api.ApiClients.Entity.Service.Models.Api; -using Api.ApiClients.Entity.Service.Models.Criterias; -using DynamicExpression.Interfaces; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.ApiClient.Requests; -using Nano.App.ApiClient.Requests.Models; -using Nano.Common.Consts; - -namespace Api.ApiClients.Entity.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, NanoApiClient nanoApiClient) : BaseController(logger) -{ - private readonly NanoApiClient nanoApiClient = nanoApiClient ?? throw new ArgumentNullException(nameof(nanoApiClient)); - - - #region Read - - /// - /// Gets all entities matching the specified query. - /// - /// The query used to filter entities. - /// Optional include depth for related entities. - /// The cancellation token. - /// A collection of entities matching the query. - /// Entities retrieved successfully. - /// Invalid query parameters. - /// Unauthorized access. - /// No entities found. - /// Internal server error. - [HttpGet] - [Route(ActionRoutes.INDEX)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task IndexAsync([FromQuery][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) - { - var examples = await this.nanoApiClient.Entity - .IndexAsync(new IndexRequest - { - IncludeDepth = includeDepth - }, cancellationToken); - - return this.Ok(examples); - } - - /// - /// Gets a single entity by its identifier. - /// - /// The identifier of the entity. - /// Optional include depth for related entities. - /// The cancellation token. - /// The entity matching the identifier. - /// Entity retrieved successfully. - /// Invalid identifier. - /// Unauthorized access. - /// Entity not found. - /// Internal server error. - [HttpGet] - [Route(ActionRoutes.DETAILS)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task DetailsAsync([FromRoute][Required] Guid id, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) - { - var examples = await this.nanoApiClient.Entity - .DetailsAsync(new DetailsRequest - { - Id = id, - IncludeDepth = includeDepth - }, cancellationToken); - - return this.Ok(examples); - } - - /// - /// Gets multiple entities by their identifiers. - /// - /// The identifiers of the entities. - /// Optional include depth for related entities. - /// The cancellation token. - /// The entities matching the identifiers. - /// Entities retrieved successfully. - /// Invalid identifiers. - /// Unauthorized access. - /// No entities found. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.DETAILS_MANY)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task DetailsManyPostAsync([FromBody][Required] Guid[] ids, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) - { - var examples = await this.nanoApiClient.Entity - .DetailsManyAsync(new DetailsManyRequest - { - Ids = ids, - IncludeDepth = includeDepth - }, cancellationToken); - - return this.Ok(examples); - } - - /// - /// Queries entities matching the specified criteria. - /// - /// The query model containing filters and criteria. - /// Optional include depth for related entities. - /// The cancellation token. - /// A collection of entities matching the criteria. - /// Entities retrieved successfully. - /// Invalid query parameters. - /// Unauthorized access. - /// No entities found. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.QUERY)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task QueryPostAsync([FromBody][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) - { - var examples = await this.nanoApiClient.Entity - .QueryAsync(new QueryRequest - { - Query = query, - IncludeDepth = includeDepth - }, cancellationToken); - - return this.Ok(examples); - } - - /// - /// Retrieves the first entity matching the specified criteria. - /// - /// The query model containing filters and criteria. - /// Optional include depth for related entities. - /// The cancellation token. - /// The first entity matching the criteria. - /// Entity retrieved successfully. - /// Invalid query parameters. - /// Unauthorized access. - /// No entity found. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.QUERY_FIRST)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task QueryFirstPostAsync([FromBody][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) - { - var example = await this.nanoApiClient.Entity - .QueryFirstAsync(new QueryFirstRequest - { - Query = query, - IncludeDepth = includeDepth - }, cancellationToken); - - return this.Ok(example); - } - - /// - /// Gets the total count of entities matching the specified criteria. - /// - /// The criteria model containing filters. - /// The cancellation token. - /// The number of entities matching the criteria. - /// Count retrieved successfully. - /// Invalid criteria parameters. - /// Unauthorized access. - /// No entities found. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.QUERY_COUNT)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task QueryCountPostAsync([FromBody][Required] ExampleQueryCriteria criteria, CancellationToken cancellationToken = default) - { - var count = await this.nanoApiClient.Entity - .QueryCountAsync(new QueryCountRequest - { - Criteria = criteria - }, cancellationToken); - - return this.Ok(count); - } - - #endregion - - - #region Create - - /// - /// Creates a single model instance. - /// - /// The entity to create. - /// Cancellation token. - /// The created entity. - /// Entity created. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.CREATE)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(Example), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task CreateAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) - { - var example = await this.nanoApiClient.Entity - .CreateAsync(new CreateRequest - { - Entity = entity - }, cancellationToken); - - return this.Created(ActionRoutes.CREATE, example); - } - - /// - /// Creates or edits a single model instance. - /// - /// The entity to create. - /// Cancellation token. - /// The created or edited entity. - /// Entity created. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.CREATE_OR_EDIT)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task CreateOrEditAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) - { - var example = await this.nanoApiClient.Entity - .CreateOrEditAsync(new CreateOrEditRequest - { - Entity = entity - }, cancellationToken); - - return this.Ok(example); - } - - /// - /// Creates a single model instance or if it already exist, retrieves it with included navigations. - /// - /// The entity to create. - /// Cancellation token. - /// The created entity with included navigations. - /// Entity created. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.CREATE_OR_GET)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(Example), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task CreateOrGetAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) - { - var example = await this.nanoApiClient.Entity - .CreateOrGetAsync(new CreateOrGetRequest - { - Entity = entity - }, cancellationToken); - - return this.Created(ActionRoutes.CREATE_OR_GET, example); - } - - /// - /// Creates a single model instance and retrieves it with included navigations. - /// - /// The entity to create. - /// Cancellation token. - /// The created entity with included navigations. - /// Entity created. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.CREATE_AND_GET)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(Example), (int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task CreateAndGetAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) - { - var example = await this.nanoApiClient.Entity - .CreateAndGetAsync(new CreateAndGetRequest - { - Entity = entity - }, cancellationToken); - - return this.Created(ActionRoutes.CREATE_AND_GET, example); - } - - /// - /// Creates multiple model instances. - /// - /// The entities to create. - /// Cancellation token. - /// Void. - /// Entities created. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.CREATE_MANY)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task CreateManyAsync([FromBody][Required] IEnumerable entities, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .CreateManyAsync(new CreateManyRequest - { - Entities = entities - }, cancellationToken); - - return this.Created(); - } - - /// - /// Creates multiple model instances in bulk. - /// - /// The entities to create. - /// Cancellation token. - /// Void. - /// Ok. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPost] - [Route(ActionRoutes.CREATE_MANY_BULK)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.Created)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task CreateManyBulkAsync([FromBody][Required] IEnumerable entities, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .CreateManyBulkAsync(new CreateManyBulkRequest - { - Entities = entities - }, cancellationToken); - - return this.Created(); - } - - #endregion - - - #region Edit - - /// - /// Edits a single model instance. - /// - /// The entity to edit. - /// Cancellation token. - /// The edited entity. - /// Entity updated. - /// Entity not found. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPut] - [Route(ActionRoutes.EDIT)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task EditAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) - { - var example = await this.nanoApiClient.Entity - .EditAsync(new EditRequest - { - Entity = entity - }, cancellationToken); - - return this.Ok(example); - } - - /// - /// Edits a single model instance and retrieves it with included navigations. - /// - /// The entity to edit. - /// Cancellation token. - /// The edited entity. - /// Entity updated. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPut] - [Route(ActionRoutes.EDIT_GET)] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task EditAndGetAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) - { - var example = await this.nanoApiClient.Entity - .EditAndGetAsync(new EditAndGetRequest - { - Entity = entity - }, cancellationToken); - - return this.Ok(example); - } - - /// - /// Edits multiple model instances. - /// - /// The entities to edit. - /// Cancellation token. - /// Void. - /// Entities updated. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPut] - [Route(ActionRoutes.EDIT_MANY)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task EditManyAsync([FromBody][Required] IEnumerable entities, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .EditManyAsync(new EditManyRequest - { - Entities = entities - }, cancellationToken); - - return this.Ok(); - } - - /// - /// Edits multiple model instances in bulk. - /// - /// The entities to edit. - /// Cancellation token. - /// Void. - /// Entities updated. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPut] - [Route(ActionRoutes.EDIT_MANY_BULK)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task EditManyBulkAsync([FromBody][Required] IEnumerable entities, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .EditManyBulkAsync(new EditManyBulkRequest - { - Entities = entities - }, cancellationToken); - - return this.Ok(); - } - - /// - /// Edits entities that match the specified criteria. - /// - /// The update query containing criteria and property updates. - /// Cancellation token. - /// Void. - /// Entities updated. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPut] - [Route(ActionRoutes.EDIT_QUERY)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task EditQueryAsync([FromBody][Required] UpdateQuery query, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .EditQueryAsync(new EditQueryRequest - { - Query = query - }, cancellationToken); - - return this.Ok(); - } - - /// - /// Edits entities that match the specified criteria in bulk. - /// - /// The update query containing criteria and property updates. - /// Cancellation token. - /// Void. - /// Entities updated. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpPut] - [Route(ActionRoutes.EDIT_QUERY_BULK)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task EditQueryBulkAsync([FromBody][Required] UpdateQuery query, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .EditQueryBulkAsync(new EditQueryBulkRequest - { - Query = query - }, cancellationToken); - - return this.Ok(); - } - - #endregion - - - #region Delete - - /// - /// Deletes a single entity by its identifier. - /// - /// The identifier of the entity to delete. - /// Cancellation token. - /// Void. - /// Entity deleted. - /// Bad request. - /// Unauthorized. - /// Entity not found. - /// Internal server error. - [HttpDelete] - [Route(ActionRoutes.DELETE)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task DeleteAsync([FromRoute][Required] Guid id, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .DeleteAsync(new DeleteRequest - { - Id = id - }, cancellationToken); - - return this.Ok(); - } - - /// - /// Deletes multiple entities by their identifiers. - /// - /// The identifiers of the entities to delete. - /// Cancellation token. - /// Void. - /// Entities deleted. - /// Bad request. - /// Unauthorized. - /// Entities not found. - /// Internal server error. - [HttpDelete] - [Route(ActionRoutes.DELETE_MANY)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task DeleteManyAsync([FromBody][Required] Guid[] ids, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .DeleteManyAsync(new DeleteManyRequest - { - Ids = ids - }, cancellationToken); - - return this.Ok(); - } - - /// - /// Deletes multiple entities by their identifiers in bulk. - /// - /// The identifiers of the entities to delete. - /// Cancellation token. - /// Void. - /// Entities deleted. - /// Bad request. - /// Unauthorized. - /// Entities not found. - /// Internal server error. - [HttpDelete] - [Route(ActionRoutes.DELETE_MANY_BULK)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NotFound)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task DeleteManyBulkAsync([FromBody][Required] Guid[] ids, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .DeleteManyBulkAsync(new DeleteManyBulkRequest - { - Ids = ids - }, cancellationToken); - - return this.Ok(); - } - - /// - /// Deletes entities matching the specified criteria. - /// - /// The criteria for selecting entities to delete. - /// Cancellation token. - /// Void. - /// Entities deleted. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpDelete] - [Route(ActionRoutes.DELETE_QUERY)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task DeleteQueryAsync([FromBody][Required] ExampleQueryCriteria select, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .DeleteQueryAsync(new DeleteQueryRequest - { - Criteria = select - }, cancellationToken); - - return this.Ok(); - } - - /// - /// Bulk deletes entities matching the specified criteria. - /// - /// The criteria for selecting entities to delete. - /// Cancellation token. - /// Void. - /// Entities deleted. - /// Bad request. - /// Unauthorized. - /// Internal server error. - [HttpDelete] - [Route(ActionRoutes.DELETE_QUERY_BULK)] - [Consumes(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task DeleteQueryBulkAsync([FromBody][Required] ExampleQueryCriteria select, CancellationToken cancellationToken = default) - { - await this.nanoApiClient.Entity - .DeleteQueryBulkAsync(new DeleteQueryBulkRequest - { - Criteria = select - }, cancellationToken); - - return this.Ok(); - } - - #endregion -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local b/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local deleted file mode 100644 index 72d7216b..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ApiClients.Entity.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Program.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Properties/InternalsVisibleTo.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 9a663cce..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ApiClients.Entity")] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Development.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Production.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Staging.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json deleted file mode 100644 index 62eb606b..00000000 --- a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Apis": { - "NanoApiClient": { - "Host": "api.apiclients.entity.service", - "Root": "api", - "Port": 8181, - "UseSsl": false, - "Timeout": "00:00:30", - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } - } - } -} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Dockerfile b/Api.ApiClients.Entity/Dockerfile deleted file mode 100644 index 961dbddb..00000000 --- a/Api.ApiClients.Entity/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.ApiClients.Entity.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Entity/LICENSE b/Api.ApiClients.Entity/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.ApiClients.Entity/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.ApiClients.Entity/README.md b/Api.ApiClients.Entity/README.md deleted file mode 100644 index eae12a82..00000000 --- a/Api.ApiClients.Entity/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Api.ApiClients.Entity - -> _Nano API application with api-client entity models._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.ApiClients](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.ApiClients)**. All the custom methods have been removed and replaced -with corresponding methods for each entity controller operation in the inner service. - -The inner application has a data provider enabled to demonstrate the generic API client integration with Nano entity models. A `NanoApiClient` implementation, derived from -`BaseApiClient`, has been added to the service application. This lesson showcases how to use the `Entity` sub-group of the `BaseApiClient` to interact with any entity model -exposed by the inner application. - -The endpoints mirror those of the entity controller in the inner service, allowing each entity action to be invoked through the API client for demonstration purposes. In a -real-world scenario, this structure would typically differ. The outer application would define its own request and response contracts tailored to its domain. However, for -simplicity and clarity in this example, the responses from the inner service are passed directly through the outer API. - -> 📖 Learn more about **[Nano Api Clients](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App#api-clients)**. diff --git a/Api.ApiClients.Entity/icon.png b/Api.ApiClients.Entity/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/.docker/docker-compose.yml b/Api.ApiClients.RootLogIn/.docker/docker-compose.yml deleted file mode 100644 index 8f0c55a8..00000000 --- a/Api.ApiClients.RootLogIn/.docker/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -services: - api.apiclients.rootlogin: - image: api.apiclients.rootlogin - hostname: api-apiclients-rootlogin - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.ApiClients.RootLogIn - dockerfile: "Dockerfile.Local" - networks: - - network - - api.apiclients.rootlogin.service: - image: api.apiclients.rootlogin.service - hostname: api-apiclients-rootlogin-service - restart: on-failure - ports: - - 8181:8181 - build: - context: ../Api.ApiClients.RootLogIn.Service - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.ApiClients.RootLogIn/.dockerignore b/Api.ApiClients.RootLogIn/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.ApiClients.RootLogIn/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.ApiClients.RootLogIn/.github/config/slack.yml b/Api.ApiClients.RootLogIn/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.ApiClients.RootLogIn/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml b/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml deleted file mode 100644 index ba5be9b0..00000000 --- a/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.ApiClients.RootLogIn - IMAGE_NAME: api.apiclients.rootlogin - SERVICE_NAME: api-apiclients-rootlogin - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/.gitignore b/Api.ApiClients.RootLogIn/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.ApiClients.RootLogIn/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/.kubernetes/autoscaler.yaml b/Api.ApiClients.RootLogIn/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.ApiClients.RootLogIn/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml b/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml b/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ApiClients.RootLogIn/.kubernetes/service.yaml b/Api.ApiClients.RootLogIn/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.ApiClients.RootLogIn/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Properties/DoNotParallelize.cs b/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Tests.Api.ApiClients.RootLogIn.csproj b/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Tests.Api.ApiClients.RootLogIn.csproj deleted file mode 100644 index faf5ada4..00000000 --- a/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Tests.Api.ApiClients.RootLogIn.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.sln b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.sln deleted file mode 100644 index 4b5b043d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.sln +++ /dev/null @@ -1,159 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.RootLogIn.Models", "Api.ApiClients.RootLogIn.Models\Api.ApiClients.RootLogIn.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.RootLogIn", "Api.ApiClients.RootLogIn\Api.ApiClients.RootLogIn.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ApiClients.RootLogIn", ".tests\Tests.Api.ApiClients.RootLogIn\Tests.Api.ApiClients.RootLogIn.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.RootLogIn.Service.Models", "Api.ApiClients.RootLogIn.Service.Models\Api.ApiClients.RootLogIn.Service.Models.csproj", "{72C75C0B-EACD-A113-B9A4-2600519A71E0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.RootLogIn.Service", "Api.ApiClients.RootLogIn.Service\Api.ApiClients.RootLogIn.Service.csproj", "{37CEC825-166A-5BB7-9466-A75FFE32383F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.Build.0 = Release|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/NanoApiClient.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/NanoApiClient.cs deleted file mode 100644 index 1c1cabe7..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/NanoApiClient.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Api.ApiClients.RootLogIn.Service.Models.ApiClient.Requests; -using Nano.App.ApiClient; -using Nano.Data.Abstractions.Identity.Authentication.Models; - -namespace Api.ApiClients.RootLogIn.Service.Models.ApiClient; - -/// -/// Nano Api Client. -/// -public class NanoApiClient(Nano.App.ApiClient.ApiClient apiClient) : BaseApiClient(apiClient) -{ - /// - /// Auto Authenticate Root Async. - /// - /// The . - /// The . - /// The . - public Task AutoAuthenticateRootAsync(AutoAuthenticateRootRequest request, CancellationToken cancellationToken = default) - { - return this.InvokeAsync(request, cancellationToken); - } -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/AutoAuthenticateRootRequest.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/AutoAuthenticateRootRequest.cs deleted file mode 100644 index a1452d56..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/AutoAuthenticateRootRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Nano.App.ApiClient.Annotations.Actions; - -namespace Api.ApiClients.RootLogIn.Service.Models.ApiClient.Requests; - -/// -/// Auto Authenticate Root Request. -/// -[GetAction("auto-authenticate-root")] -public class AutoAuthenticateRootRequest : BaseCustomsRequest; \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/BaseCustomsRequest.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/BaseCustomsRequest.cs deleted file mode 100644 index 05bf4544..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/BaseCustomsRequest.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Nano.App.ApiClient.Requests; - -namespace Api.ApiClients.RootLogIn.Service.Models.ApiClient.Requests; - -/// -/// Base Customs Request (abstract). -/// -public abstract class BaseCustomsRequest : BaseRequest -{ - /// - /// Constructor. - /// - protected BaseCustomsRequest() - { - this.Controller = "customs"; - } -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Api.ApiClients.RootLogin.Service.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Api.ApiClients.RootLogin.Service.csproj deleted file mode 100644 index b2183917..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Api.ApiClients.RootLogin.Service.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/AuthController.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/AuthController.cs deleted file mode 100644 index dabd8009..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/AuthController.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Api.Mvc.Authentication.Abstractions; - -namespace Api.ApiClients.RootLogIn.Service.Controllers; - -/// -public class AuthController(ILogger logger, IAuthRepository authRepository) - : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/CustomsController.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/CustomsController.cs deleted file mode 100644 index 5cf39f1a..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/CustomsController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions.Identity.Authentication.Models; -using Nano.Data.Abstractions.Identity.Extensions; - -namespace Api.ApiClients.RootLogIn.Service.Controllers; - -/// -/// Customs Controller. -/// -/// The . -public class CustomsController(ILogger logger) : BaseController(logger) -{ - /// - /// Auto Authenticate Root Action. - /// - /// The cancellation token. - /// The access token. - /// Success. - [HttpGet] - [Route("auto-authenticate-root")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task AutoAuthenticateRootAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - var accessToken = this.HttpContext - .GetJwtToken(); - - return this.Ok(new AccessToken - { - Token = accessToken! - }); - } -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local deleted file mode 100644 index e6afa324..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Program.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Properties/InternalsVisibleTo.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 5d1d7f6d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ApiClients.RootLogin")] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Development.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Production.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Staging.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json deleted file mode 100644 index f28ebab1..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8181 - ] - } - }, - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", - "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV", - "Expiration": "24:00:00", - "RootLogin": { - "Username": "admin@domain.com", - "Password": "abc12|+d34DadD" - } - } - } - } -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Api.ApiClients.RootLogIn.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Api.ApiClients.RootLogIn.csproj deleted file mode 100644 index aabad09a..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Api.ApiClients.RootLogIn.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Controllers/ExamplesController.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Controllers/ExamplesController.cs deleted file mode 100644 index 05dda3f6..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Controllers/ExamplesController.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.ApiClients.RootLogIn.Service.Models.ApiClient; -using Api.ApiClients.RootLogIn.Service.Models.ApiClient.Requests; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Common.Consts; -using Nano.Data.Abstractions.Identity.Authentication.Models; - -namespace Api.ApiClients.RootLogIn.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, NanoApiClient nanoApiClient) : BaseController(logger) -{ - private readonly NanoApiClient nanoApiClient = nanoApiClient ?? throw new ArgumentNullException(nameof(nanoApiClient)); - - /// - /// Auto Authenticate Root. - /// - /// The cancellation token. - /// The access token. - /// Success. - [HttpGet] - [Route("auto-authenticate-root")] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(AccessToken), (int)HttpStatusCode.OK)] - public virtual async Task AutoAuthenticateRootAsync(CancellationToken cancellationToken = default) - { - var response = await this.nanoApiClient - .AutoAuthenticateRootAsync(new AutoAuthenticateRootRequest(), cancellationToken); - - return this.Ok(response); - } -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local deleted file mode 100644 index 557a434d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ApiClients.RootLogIn.dll"] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Program.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Properties/InternalsVisibleTo.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 98b32be2..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ApiClients.RootLogIn")] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Development.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Production.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Staging.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.json deleted file mode 100644 index 81fbdd51..00000000 --- a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { }, - "Apis": { - "NanoApiClient": { - "Host": "api.apiclients.rootlogin.service", - "Root": "api", - "Port": 8181, - "UseSsl": false, - "Timeout": "00:00:30", - "LogInRoot": { - "Username": "admin@domain.com", - "Password": "abc12|+d34DadD" - }, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } - } - } -} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Dockerfile b/Api.ApiClients.RootLogIn/Dockerfile deleted file mode 100644 index 2fad135c..00000000 --- a/Api.ApiClients.RootLogIn/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.ApiClients.RootLogIn.dll"] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/LICENSE b/Api.ApiClients.RootLogIn/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.ApiClients.RootLogIn/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/README.md b/Api.ApiClients.RootLogIn/README.md deleted file mode 100644 index fb33d5c9..00000000 --- a/Api.ApiClients.RootLogIn/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Api.ApiClients.RootLogIn - -> _Nano API application with api-client root authentication._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.ApiClients](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.ApiClients)**. All custom methods have been removed and replaced -with a new method that triggers the configured root login. - -The service is configured with Root Login and JWT authentication enabled, along with a concrete implementation of `BaseAuthController`. When invoking methods through the -API client, the automatic root login mechanism is triggered. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ------------------------------------------------------------ | -------------------------------------- | -| `http://localhost:8080/api/examples/auto-authenticate-root` | Returns a `200 OK` response. Uses the API client’s automatic root login to obtain and return an access token. | - -> 📖 Learn more about **[Nano Api Clients](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App#api-clients)**. diff --git a/Api.ApiClients.RootLogIn/icon.png b/Api.ApiClients.RootLogIn/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.ApiClients/.docker/docker-compose.yml b/Api.ApiClients/.docker/docker-compose.yml deleted file mode 100644 index 0129b3b3..00000000 --- a/Api.ApiClients/.docker/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -services: - api.apiclients: - image: api.apiclients - hostname: api-apiclients - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.ApiClients - dockerfile: "Dockerfile.Local" - networks: - - network - - api.apiclients.service: - image: api.apiclients.service - hostname: api-apiclients-service - restart: on-failure - ports: - - 8181:8181 - build: - context: ../Api.ApiClients.Service - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.ApiClients/.dockerignore b/Api.ApiClients/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.ApiClients/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.ApiClients/.github/config/slack.yml b/Api.ApiClients/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.ApiClients/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ApiClients/.github/workflows/build-and-deploy.yml b/Api.ApiClients/.github/workflows/build-and-deploy.yml deleted file mode 100644 index b51947ec..00000000 --- a/Api.ApiClients/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.ApiClients - IMAGE_NAME: api.apiclients - SERVICE_NAME: api-apiclients - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ApiClients/.gitignore b/Api.ApiClients/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.ApiClients/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ApiClients/.kubernetes/autoscaler.yaml b/Api.ApiClients/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.ApiClients/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ApiClients/.kubernetes/configmap.yaml b/Api.ApiClients/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.ApiClients/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ApiClients/.kubernetes/deployment.yaml b/Api.ApiClients/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.ApiClients/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ApiClients/.kubernetes/service.yaml b/Api.ApiClients/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.ApiClients/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.ApiClients/.tests/Tests.Api.ApiClients/Properties/DoNotParallelize.cs b/Api.ApiClients/.tests/Tests.Api.ApiClients/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.ApiClients/.tests/Tests.Api.ApiClients/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ApiClients/.tests/Tests.Api.ApiClients/Tests.Api.ApiClients.csproj b/Api.ApiClients/.tests/Tests.Api.ApiClients/Tests.Api.ApiClients.csproj deleted file mode 100644 index 2cd69c49..00000000 --- a/Api.ApiClients/.tests/Tests.Api.ApiClients/Tests.Api.ApiClients.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj b/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj b/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/NanoApiClient.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/NanoApiClient.cs deleted file mode 100644 index 849b28b9..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/NanoApiClient.cs +++ /dev/null @@ -1,92 +0,0 @@ -using Nano.App.ApiClient; -using System; -using System.Threading; -using System.Threading.Tasks; -using Api.ApiClients.Service.Models.Api.Requests; -using Api.ApiClients.Service.Models.Api.Responses; - -namespace Api.ApiClients.Service.Models.Api; - -/// -/// Nano Api Client. -/// -public class NanoApiClient(ApiClient apiClient) : BaseApiClient(apiClient) -{ - /// - /// Custom Async. - /// - /// The . - /// The . - /// The . - public Task CustomAsync(CustomRequest request, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(request); - - return this.InvokeAsync(request, cancellationToken); - } - - /// - /// Custom File Async. - /// - /// The . - /// The . - /// The . - public Task CustomFileAsync(CustomFileRequest request, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(request); - - return this.InvokeAsync(request, cancellationToken); - } - - /// - /// Custom File Body Async. - /// - /// The . - /// The . - /// The . - public Task CustomFileBodyAsync(CustomFileBodyRequest request, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(request); - - return this.InvokeAsync(request, cancellationToken); - } - - /// - /// Bad Request Exception Async. - /// - /// The . - /// The . - /// Nothing. - public Task BadRequestExceptionAsync(BadRequestExceptionRequest request, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(request); - - return this.InvokeAsync(request, cancellationToken); - } - - /// - /// Problem Details Exception Async. - /// - /// The . - /// The . - /// Nothing. - public Task ProblemDetailsExceptionAsync(ProblemDetailsExceptionRequest request, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(request); - - return this.InvokeAsync(request, cancellationToken); - } - - /// - /// Request Tracing Async. - /// - /// The . - /// The . - /// The . - public Task RequestTracingAsync(RequestTracingRequest request, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(request); - - return this.InvokeAsync(request, cancellationToken); - } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BadRequestExceptionRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BadRequestExceptionRequest.cs deleted file mode 100644 index 579f9189..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BadRequestExceptionRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Nano.App.ApiClient.Annotations.Actions; - -namespace Api.ApiClients.Service.Models.Api.Requests; - -/// -/// Bad Request Exception Request. -/// -[GetAction("bad-request-exception")] -public class BadRequestExceptionRequest : BaseCustomsRequest; \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BaseCustomsRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BaseCustomsRequest.cs deleted file mode 100644 index f1b6de94..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BaseCustomsRequest.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Nano.App.ApiClient.Requests; - -namespace Api.ApiClients.Service.Models.Api.Requests; - -/// -/// Base Customs Request (abstract). -/// -public abstract class BaseCustomsRequest : BaseRequest -{ - /// - /// Constructor. - /// - protected BaseCustomsRequest() - { - this.Controller = "customs"; - } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileBodyRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileBodyRequest.cs deleted file mode 100644 index a8902bdf..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileBodyRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Api.ApiClients.Service.Models.Api.Requests.Models; -using Nano.App.ApiClient.Annotations; -using Nano.App.ApiClient.Annotations.Actions; -using Nano.App.ApiClient.Models; - -namespace Api.ApiClients.Service.Models.Api.Requests; - -/// -/// Custom File Body Request. -/// -[PostAction("custom/file/body")] -public class CustomFileBodyRequest : BaseCustomsRequest -{ - /// - /// File. - /// - [Form] - public required NamedStream File { get; set; } - - /// - /// Body. - /// - [Form] - public required CustomFileBody Body { get; set; } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileRequest.cs deleted file mode 100644 index 0793c7c7..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileRequest.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Nano.App.ApiClient.Annotations; -using Nano.App.ApiClient.Annotations.Actions; -using Nano.App.ApiClient.Models; - -namespace Api.ApiClients.Service.Models.Api.Requests; - -/// -/// Custom File Request. -/// -[PostAction("custom/file")] -public class CustomFileRequest : BaseCustomsRequest -{ - /// - /// File. - /// - [Form] - public required NamedStream File { get; set; } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomRequest.cs deleted file mode 100644 index f5d5471e..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomRequest.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using Api.ApiClients.Service.Models.Api.Requests.Models; -using Nano.App.ApiClient.Annotations; -using Nano.App.ApiClient.Annotations.Actions; - -namespace Api.ApiClients.Service.Models.Api.Requests; - -/// -/// Custom Request. -/// -[PostAction("{id}/{type}")] -public class CustomRequest : BaseCustomsRequest -{ - /// - /// Id. - /// - [Route(Order = 0)] - public required Guid Id { get; set; } - - /// - /// Type. - /// - [Route(Order = 1)] - public required string Type { get; set; } - - /// - /// Body. - /// - [Body] - public required CustomBody Body { get; set; } - - /// - /// Query. - /// - [Query] - public required DateTimeOffset Query { get; set; } - - /// - /// Header. - /// - [Header(Name = "X-Custom-Header")] - public required string Header { get; set; } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomBody.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomBody.cs deleted file mode 100644 index 7b46ad3b..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomBody.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Api.ApiClients.Service.Models.Api.Requests.Models; - -/// -/// Custom Body. -/// -public class CustomBody -{ - /// - /// Name. - /// - public string? Name { get; set; } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomFileBody.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomFileBody.cs deleted file mode 100644 index 3ba7f0e3..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomFileBody.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Api.ApiClients.Service.Models.Api.Requests.Models; - -/// -/// Custom File Body. -/// -public class CustomFileBody -{ - /// - /// Text. - /// - [Required] - public string Text { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/ProblemDetailsExceptionRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/ProblemDetailsExceptionRequest.cs deleted file mode 100644 index a4d9ac00..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/ProblemDetailsExceptionRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Nano.App.ApiClient.Annotations.Actions; - -namespace Api.ApiClients.Service.Models.Api.Requests; - -/// -/// Problem Details Exception Request. -/// -[GetAction("problem-details-exception")] -public class ProblemDetailsExceptionRequest : BaseCustomsRequest; \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/RequestTracingRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/RequestTracingRequest.cs deleted file mode 100644 index f39993d8..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/RequestTracingRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Nano.App.ApiClient.Annotations.Actions; - -namespace Api.ApiClients.Service.Models.Api.Requests; - -/// -/// Problem Details Exception Request. -/// -[GetAction("request-tracing")] -public class RequestTracingRequest : BaseCustomsRequest; \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileBodyResponse.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileBodyResponse.cs deleted file mode 100644 index b87275f6..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileBodyResponse.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Api.ApiClients.Service.Models.Api.Requests.Models; - -namespace Api.ApiClients.Service.Models.Api.Responses; - -/// -/// Custom File Body Response. -/// -public class CustomFileBodyResponse : CustomFileResponse -{ - /// - /// Body. - /// - public required CustomFileBody Body { get; set; } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileResponse.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileResponse.cs deleted file mode 100644 index 81e80282..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Api.ApiClients.Service.Models.Api.Responses; - -/// -/// Custom File Response. -/// -public class CustomFileResponse -{ - /// - /// Filename. - /// - public required string Filename { get; set; } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomResponse.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomResponse.cs deleted file mode 100644 index 2288031c..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomResponse.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Api.ApiClients.Service.Models.Api.Requests; -using System; -using Api.ApiClients.Service.Models.Api.Requests.Models; - -namespace Api.ApiClients.Service.Models.Api.Responses; - -/// -/// Custom Response. -/// -public class CustomResponse -{ - /// - /// Id. - /// - public required Guid Id { get; set; } - - /// - /// Type. - /// - public required string Type { get; set; } - - /// - /// Body. - /// - public required CustomBody Body { get; set; } - - /// - /// Query. - /// - public required DateTimeOffset? Query { get; set; } - - /// - /// Header. - /// - public required string Header { get; set; } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/RequestTracingResponse.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/RequestTracingResponse.cs deleted file mode 100644 index ef71234a..00000000 --- a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/RequestTracingResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Api.ApiClients.Service.Models.Api.Responses; - -/// -/// Request Tracing Response. -/// -public class RequestTracingResponse -{ - /// - /// Request Id. - /// - public required string RequestId { get; set; } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/Api.ApiClients.Service.csproj b/Api.ApiClients/Api.ApiClients.Service/Api.ApiClients.Service.csproj deleted file mode 100644 index 6e1bedf1..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/Api.ApiClients.Service.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/Controllers/CustomsController.cs b/Api.ApiClients/Api.ApiClients.Service/Controllers/CustomsController.cs deleted file mode 100644 index 7c82ca0c..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/Controllers/CustomsController.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Api.ApiClients.Service.Models.Api.Responses; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Annotations; -using Nano.App.Api.Controllers; -using Nano.App.Exceptions; -using Nano.Common.Consts; -using System; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.ApiClients.Service.Models.Api.Requests.Models; - -namespace Api.ApiClients.Service.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class CustomsController(ILogger logger) : BaseController(logger) -{ - /// - /// Custom Action. - /// - /// The first route parameter. - /// The second route parameter. - /// The request body. - /// The querystring parameter. - /// The header value - /// The cancellation token. - /// A custom response. - /// Success. - [HttpPost] - [Route("{id:guid}/{type}")] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(CustomResponse), (int)HttpStatusCode.OK)] - public virtual async Task CustomAsync([FromRoute][Required] Guid id, [FromRoute][Required] string type, [FromBody][Required] CustomBody body, [FromQuery][Required] DateTimeOffset query, [FromHeader(Name = "X-Custom-Header")][Required] string header, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok(new CustomResponse - { - Id = id, - Type = type, - Body = body, - Query = query, - Header = header - }); - } - - /// - /// Custom File Action. - /// - /// The file. - /// The cancellation token. - /// The custom file response. - /// Success. - [HttpPost] - [Route("custom/file")] - [Consumes(HttpContentType.FORM)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(CustomFileResponse), (int)HttpStatusCode.OK)] - public virtual async Task CustomFileAsync(IFormFile file, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok(new CustomFileResponse - { - Filename = file.FileName - }); - } - - /// - /// Custom File Body Action. - /// - /// The file. - /// The json body - /// The cancellation token. - /// The custom file body response. - /// Success. - [HttpPost] - [Route("custom/file/body")] - [Consumes(HttpContentType.FORM)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(CustomFileBodyResponse), (int)HttpStatusCode.OK)] - public virtual async Task CustomFileBodyAsync(IFormFile file, [Required][FromFormBody] CustomFileBody body, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok(new CustomFileBodyResponse - { - Filename = file.FileName, - Body = body - }); - } - - /// - /// Custom Bad Request Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Bad Request. - [HttpGet] - [Route("bad-request-exception")] - [Produces(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public virtual async Task CustomBadRequestExceptionAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new BadRequestException("bad request.", true, true); - } - - /// - /// Custom Problem Details Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Expectation Failed. - [HttpGet] - [Route("problem-details-exception")] - [Produces(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.ExpectationFailed)] - public virtual async Task CustomProblemDetailsExceptionAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new ProblemDetailsException(new ProblemDetails - { - Type = "https://datatracker.ietf.org/doc/html/rfc9110#name-417-expectation-failed", - Title = "Expectation Failed", - Status = (int)HttpStatusCode.ExpectationFailed - }); - } - - /// - /// Request Tracing Action. - /// - /// The cancellation token. - /// The request tracing response. - /// Success. - [HttpGet] - [Route("request-tracing")] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(RequestTracingResponse), (int)HttpStatusCode.OK)] - public virtual async Task RequestTracingAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok(new RequestTracingResponse - { - RequestId = this.HttpContext.TraceIdentifier - }); - } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local b/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local deleted file mode 100644 index 5d914dde..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ApiClients.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/Program.cs b/Api.ApiClients/Api.ApiClients.Service/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ApiClients/Api.ApiClients.Service/Properties/InternalsVisibleTo.cs b/Api.ApiClients/Api.ApiClients.Service/Properties/InternalsVisibleTo.cs deleted file mode 100644 index e3560176..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ApiClients")] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.Development.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.Production.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.Staging.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.json deleted file mode 100644 index 702a5e62..00000000 --- a/Api.ApiClients/Api.ApiClients.Service/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8181 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.sln b/Api.ApiClients/Api.ApiClients.sln deleted file mode 100644 index 3d35e9ea..00000000 --- a/Api.ApiClients/Api.ApiClients.sln +++ /dev/null @@ -1,159 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Models", "Api.ApiClients.Models\Api.ApiClients.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients", "Api.ApiClients\Api.ApiClients.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ApiClients", ".tests\Tests.Api.ApiClients\Tests.Api.ApiClients.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Service.Models", "Api.ApiClients.Service.Models\Api.ApiClients.Service.Models.csproj", "{72C75C0B-EACD-A113-B9A4-2600519A71E0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Service", "Api.ApiClients.Service\Api.ApiClients.Service.csproj", "{37CEC825-166A-5BB7-9466-A75FFE32383F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.Build.0 = Release|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.ApiClients/Api.ApiClients/Api.ApiClients.csproj b/Api.ApiClients/Api.ApiClients/Api.ApiClients.csproj deleted file mode 100644 index 30feaf81..00000000 --- a/Api.ApiClients/Api.ApiClients/Api.ApiClients.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/Controllers/ExamplesController.cs b/Api.ApiClients/Api.ApiClients/Controllers/ExamplesController.cs deleted file mode 100644 index cfc94ee1..00000000 --- a/Api.ApiClients/Api.ApiClients/Controllers/ExamplesController.cs +++ /dev/null @@ -1,184 +0,0 @@ -using Api.ApiClients.Service.Models.Api; -using Api.ApiClients.Service.Models.Api.Requests; -using Api.ApiClients.Service.Models.Api.Requests.Models; -using Api.ApiClients.Service.Models.Api.Responses; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Annotations; -using Nano.App.Api.Controllers; -using Nano.Common.Consts; -using System; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Nano.App.ApiClient.Models; - -namespace Api.ApiClients.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, NanoApiClient nanoApiClient) : BaseController(logger) -{ - private readonly NanoApiClient nanoApiClient = nanoApiClient ?? throw new ArgumentNullException(nameof(nanoApiClient)); - - /// - /// Custom. - /// - /// The first route parameter. - /// The second route parameter. - /// The request body. - /// The querystring parameter. - /// The header value - /// The cancellation token. - /// The custom response. - /// Success. - [HttpPost] - [Route("custom/{id:guid}/{type}")] - [Consumes(HttpContentType.JSON)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(CustomResponse), (int)HttpStatusCode.OK)] - public virtual async Task CustomAsync([FromRoute][Required] Guid id, [FromRoute][Required] string type, [FromBody][Required] CustomBody body, [FromQuery][Required] DateTimeOffset query, [FromHeader(Name = "X-Custom-Header")][Required] string header, CancellationToken cancellationToken = default) - { - var response = await this.nanoApiClient - .CustomAsync(new CustomRequest - { - Id = id, - Type = type, - Body = body, - Query = query, - Header = header - }, cancellationToken); - - if (response == null) - { - return this.NotFound(); - } - - return this.Ok(response); - } - - /// - /// Custom File Action. - /// - /// The file. - /// The cancellation token. - /// The custom file response. - /// Success. - [HttpPost] - [Route("custom/file")] - [Consumes(HttpContentType.FORM)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(CustomFileResponse), (int)HttpStatusCode.OK)] - public virtual async Task CustomFileAsync(IFormFile file, CancellationToken cancellationToken = default) - { - var response = await this.nanoApiClient - .CustomFileAsync(new CustomFileRequest - { - File = new NamedStream - { - Name = file.FileName, - Stream = file.OpenReadStream() - } - }, cancellationToken); - - if (response == null) - { - return this.NotFound(); - } - - return this.Ok(response); - } - - /// - /// Custom File Body Action. - /// - /// The file. - /// The json body - /// The cancellation token. - /// The custom file body response. - /// Success. - [HttpPost] - [Route("custom/file/body")] - [Consumes(HttpContentType.FORM)] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(CustomFileResponse), (int)HttpStatusCode.OK)] - public virtual async Task CustomFileBodyAsync(IFormFile file, [Required][FromFormBody] CustomFileBody body, CancellationToken cancellationToken = default) - { - var response = await this.nanoApiClient - .CustomFileBodyAsync(new CustomFileBodyRequest - { - File = new NamedStream - { - Name = file.FileName, - Stream = file.OpenReadStream() - }, - Body = body - }, cancellationToken); - - if (response == null) - { - return this.NotFound(); - } - - return this.Ok(response); - } - - /// - /// Bad Request. - /// - /// The cancellation token. - /// Nothings. - /// Bad Request. - [HttpGet] - [Route("bad-request-exception")] - [Produces(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public virtual async Task BadRequestAsync(CancellationToken cancellationToken = default) - { - await this.nanoApiClient - .BadRequestExceptionAsync(new BadRequestExceptionRequest(), cancellationToken); - - return this.Ok(); - } - - /// - /// Problem Details Exception. - /// - /// The cancellation token. - /// Nothings. - /// Expectation Failed. - [HttpGet] - [Route("problem-details-exception")] - [Produces(HttpContentType.JSON)] - [ProducesResponseType((int)HttpStatusCode.ExpectationFailed)] - public virtual async Task ProblemDetailsExceptionAsync(CancellationToken cancellationToken = default) - { - await this.nanoApiClient - .ProblemDetailsExceptionAsync(new ProblemDetailsExceptionRequest(), cancellationToken); - - return this.Ok(); - } - - /// - /// Request Tracing Exception. - /// - /// The cancellation token. - /// The request tracing response. - /// Success. - [HttpGet] - [Route("request-tracing")] - [Produces(HttpContentType.JSON)] - [ProducesResponseType(typeof(RequestTracingResponse), (int)HttpStatusCode.OK)] - public virtual async Task RequestTracingAsync(CancellationToken cancellationToken = default) - { - var response = await this.nanoApiClient - .RequestTracingAsync(new RequestTracingRequest(), cancellationToken); - - return this.Ok(response); - } -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/Dockerfile.Local b/Api.ApiClients/Api.ApiClients/Dockerfile.Local deleted file mode 100644 index ce847262..00000000 --- a/Api.ApiClients/Api.ApiClients/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ApiClients.dll"] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/Program.cs b/Api.ApiClients/Api.ApiClients/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ApiClients/Api.ApiClients/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ApiClients/Api.ApiClients/Properties/InternalsVisibleTo.cs b/Api.ApiClients/Api.ApiClients/Properties/InternalsVisibleTo.cs deleted file mode 100644 index e3560176..00000000 --- a/Api.ApiClients/Api.ApiClients/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ApiClients")] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/appsettings.Development.json b/Api.ApiClients/Api.ApiClients/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients/Api.ApiClients/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/appsettings.Production.json b/Api.ApiClients/Api.ApiClients/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients/Api.ApiClients/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/appsettings.Staging.json b/Api.ApiClients/Api.ApiClients/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ApiClients/Api.ApiClients/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/appsettings.json b/Api.ApiClients/Api.ApiClients/appsettings.json deleted file mode 100644 index 07cd49d9..00000000 --- a/Api.ApiClients/Api.ApiClients/appsettings.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { }, - "Apis": { - "NanoApiClient": { - "Host": "api.apiclients.service", - "Root": "api", - "Port": 8181, - "UseSsl": false, - "Timeout": "00:00:30", - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } - } - } -} \ No newline at end of file diff --git a/Api.ApiClients/Dockerfile b/Api.ApiClients/Dockerfile deleted file mode 100644 index 554c9c3f..00000000 --- a/Api.ApiClients/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.ApiClients.dll"] \ No newline at end of file diff --git a/Api.ApiClients/LICENSE b/Api.ApiClients/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.ApiClients/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.ApiClients/README.md b/Api.ApiClients/README.md deleted file mode 100644 index dd590d79..00000000 --- a/Api.ApiClients/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# Api.ApiClients - -> _Nano API application with api-clients._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)**. - -This lesson demonstrates how to connect one API application to another using Nano’s built-in API client, enabling seamless communication between services. - -A `NanoApiClient` implementation, derived from `BaseApiClient`, has been added to the inner service application. It includes several methods that demonstrate different -features of the Nano API client. These methods are in turn exposed through corresponding endpoints in the `ExamplesController` of the outer API application. In addition, -the outer application has been configured to include the API client, enabling it to communicate with the inner service application. - -A health check is configured to target the application of the api-client. -Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#health-checks)**. - -The following endpoint is available for testing. - -| Endpoint | Description | -| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/custom/{id:guid}/{type}` | Returns a `200 OK` response. The provided route, query, header, and body variables are forwarded to the inner service and returned in the response. | -| `http://localhost:8080/api/examples/custom/file` | Returns a `200 OK` response. A file is uploaded via the API client, and the uploaded filename is returned in the response. | -| `http://localhost:8080/api/examples/custom/file/body` | Returns a `200 OK` response. A file and form data are uploaded via the API client, and both the filename and request body are returned in the response. | -| `http://localhost:8080/api/examples/bad-request-exception` | Returns a `400 Bad Request` response. A `BadRequestException` is intentionally thrown to demonstrate error handling. | -| `http://localhost:8080/api/examples/problem-details-exception` | Returns a `417 Expectation Failed` response. A `ProblemDetailsException` is thrown to demonstrate structured error handling using Problem Details. | -| `http://localhost:8080/api/examples/request-tracing` | Returns a `200 OK` response. The `X-Request-Id` header is extracted from the request and returned in the response for traceability purposes. | - -> 📖 Learn more about **[Nano Api Clients](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App#api-clients)**. - -## Configuration -Configured the application with a connection to the `NanoApiClient`. - -```json -"App": { - "Apis": { - "NanoApiClient": { - "Host": "localhost", - "Root": "api", - "Port": 8181, - "UseSsl": false, - "Timeout": "00:00:30", - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } - } -} -``` diff --git a/Api.ApiClients/icon.png b/Api.ApiClients/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Auth.External.Custom/.docker/docker-compose.yml b/Api.Auth.External.Custom/.docker/docker-compose.yml deleted file mode 100644 index c81609f9..00000000 --- a/Api.Auth.External.Custom/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.auth.external.custom: - image: api.auth.external.custom - hostname: api-auth-external-custom - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Auth.External.Custom - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Auth.External.Custom/.dockerignore b/Api.Auth.External.Custom/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Auth.External.Custom/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Auth.External.Custom/.github/config/slack.yml b/Api.Auth.External.Custom/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Auth.External.Custom/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml b/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml deleted file mode 100644 index fe2c756a..00000000 --- a/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,179 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Auth.External.Custom - IMAGE_NAME: api.auth.external.custom - SERVICE_NAME: api-auth-external-custom - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Auth.External.Custom/.gitignore b/Api.Auth.External.Custom/.gitignore deleted file mode 100644 index ccb0126f..00000000 --- a/Api.Auth.External.Custom/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env \ No newline at end of file diff --git a/Api.Auth.External.Custom/.kubernetes/autoscaler.yaml b/Api.Auth.External.Custom/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Auth.External.Custom/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Auth.External.Custom/.kubernetes/configmap.yaml b/Api.Auth.External.Custom/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Auth.External.Custom/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Auth.External.Custom/.kubernetes/deployment.yaml b/Api.Auth.External.Custom/.kubernetes/deployment.yaml deleted file mode 100644 index 5041e227..00000000 --- a/Api.Auth.External.Custom/.kubernetes/deployment.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Auth.External.Custom/.kubernetes/service.yaml b/Api.Auth.External.Custom/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Auth.External.Custom/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Properties/DoNotParallelize.cs b/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Tests.Api.Auth.External.Custom.csproj b/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Tests.Api.Auth.External.Custom.csproj deleted file mode 100644 index 9609bb01..00000000 --- a/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Tests.Api.Auth.External.Custom.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj b/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom.sln b/Api.Auth.External.Custom/Api.Auth.External.Custom.sln deleted file mode 100644 index 8b7813f6..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Auth.External.Custom.Models", "Api.Auth.External.Custom.Models\Api.Auth.External.Custom.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Auth.External.Custom", "Api.Auth.External.Custom\Api.Auth.External.Custom.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Auth.External.Custom", ".tests\Tests.Api.Auth.External.Custom\Tests.Api.Auth.External.Custom.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Api.Auth.External.Custom.csproj b/Api.Auth.External.Custom/Api.Auth.External.Custom/Api.Auth.External.Custom.csproj deleted file mode 100644 index 7c314376..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/Api.Auth.External.Custom.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs deleted file mode 100644 index 32b5f849..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Nano.App.Api.Mvc.Authentication; -using Nano.Data.Abstractions.Identity.Authentication.Models; - -namespace Api.Auth.External.Custom.Authentication; - -/// -public class ExternalProviderCustomRepository() : BaseAuthExternalRepository("Custom") -{ - /// - public override async Task AuthenticateAsync(ImplicitFlow flow, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return new ExternalAuthenticationData - { - Id = "external-id", - Username = "MyUser", - EmailAddress = "johndoe@domain.com", - PhoneNumber = "+4520111112", - Name = "John Doe", - ExternalToken = - { - Name = this.ProviderName, - Token = "token", - RefreshToken = "refresh-token" - } - }; - } - - /// - public override async Task AuthenticateRefreshAsync(string refreshToken, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return new ExternalAuthenticationToken - { - Name = this.ProviderName, - Token = "token", - RefreshToken = "refresh-token" - }; - } -} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/AuthController.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/AuthController.cs deleted file mode 100644 index 27db3eba..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/AuthController.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Api.Mvc.Authentication.Abstractions; - -namespace Api.Auth.External.Custom.Controllers; - -/// -public class AuthController(ILogger logger, IAuthRepository authRepository) - : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/ExamplesController.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/ExamplesController.cs deleted file mode 100644 index f466e1d1..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Auth.External.Custom.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Authenticated Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("authenticated")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task AuthenticatedAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("authenticated"); - } -} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local b/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local deleted file mode 100644 index 4d00259a..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Program.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Properties/InternalsVisibleTo.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Properties/InternalsVisibleTo.cs deleted file mode 100644 index e194e1bf..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Auth.External.Custom")] \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Development.json b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Development.json deleted file mode 100644 index 90d97ba5..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Development.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", - "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV", - "Expiration": "24:00:00" - } - } - } -} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Production.json b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Staging.json b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.json b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.json deleted file mode 100644 index 1af6db64..00000000 --- a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Documentation": { - }, - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - } - } -} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Dockerfile b/Api.Auth.External.Custom/Dockerfile deleted file mode 100644 index 2a07e09b..00000000 --- a/Api.Auth.External.Custom/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Auth.External.Custom/LICENSE b/Api.Auth.External.Custom/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Auth.External.Custom/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Auth.External.Custom/README.md b/Api.Auth.External.Custom/README.md deleted file mode 100644 index 2a462bbe..00000000 --- a/Api.Auth.External.Custom/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# Api.Auth.External.Custom - -> _Nano API application with transient custom external authentication._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#summary) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a derived `AuthController` as well as a simple test controller -that inherits from the top-level Nano `BaseController`. - -The JWT authentication scheme has been configured, and a `BaseAuthExternalRepository` implementation named `ExternalProviderCustomRepository`, with `Custom` as provider-name, -has been added. As a result, additional endpoints from the `AuthController` are now exposed, as shown below. - -The `ExternalProviderCustomRepository` always succeeds and returns static (mocked) data. It serves as a simple example of how to implement a custom external authentication -provider in Nano. - -API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration -are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -The following endpoint from the auth controller is available for testing. - -| Endpoint | Description | -| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -| `http://localhost:8080/api/auth/external/schemes` | Retrieves all configured external authentication methods, e.g., Google, Facebook. | -| `http://localhost:8080/api/auth/login/external/custom/transient` | Signs in a user using external custom authentication transient | - -Additionally, the following endpoint is available for testing authorization. - -| Endpoint | Description | -| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/authenticate` | Returns a simple `200 OK` response, when JWT authorization is successful, and otherwise a `401 Unauthorized`. | - -> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#authentication)**. - -## Configuration -Configured the application with the necessary authentication setup. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - } -} -``` - -...and `appsettings.Development.json`. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", - "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", - "Expiration": "24:00:00" - } - } -} -``` - -## Kubernetes -For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be -enabled. Below demonstrates how to map the secret containing the JWT keys. - -```yaml -spec: - template: - spec: - containers: - env: - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key -``` - -## GitHub Action -The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. - -```yaml -env: - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} -``` - -...and created during the Kubernetes deploy step. - -```yaml -sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Api.Auth.External.Custom/icon.png b/Api.Auth.External.Custom/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Auth.RootLogin/.docker/docker-compose.yml b/Api.Auth.RootLogin/.docker/docker-compose.yml deleted file mode 100644 index 312e415c..00000000 --- a/Api.Auth.RootLogin/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.auth.rootlogin: - image: api.auth.rootlogin - hostname: api-auth-rootlogin - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Auth.RootLogin - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Auth.RootLogin/.dockerignore b/Api.Auth.RootLogin/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Auth.RootLogin/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Auth.RootLogin/.github/config/slack.yml b/Api.Auth.RootLogin/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Auth.RootLogin/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml b/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml deleted file mode 100644 index bce63945..00000000 --- a/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,179 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Auth.RootLogin - IMAGE_NAME: api.auth.rootlogin - SERVICE_NAME: api-auth-rootlogin - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Auth.RootLogin/.gitignore b/Api.Auth.RootLogin/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Auth.RootLogin/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Auth.RootLogin/.kubernetes/autoscaler.yaml b/Api.Auth.RootLogin/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Auth.RootLogin/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Auth.RootLogin/.kubernetes/configmap.yaml b/Api.Auth.RootLogin/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Auth.RootLogin/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Auth.RootLogin/.kubernetes/deployment.yaml b/Api.Auth.RootLogin/.kubernetes/deployment.yaml deleted file mode 100644 index 5041e227..00000000 --- a/Api.Auth.RootLogin/.kubernetes/deployment.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Auth.RootLogin/.kubernetes/service.yaml b/Api.Auth.RootLogin/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Auth.RootLogin/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Properties/DoNotParallelize.cs b/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Tests.Api.Auth.RootLogin.csproj b/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Tests.Api.Auth.RootLogin.csproj deleted file mode 100644 index 78fc26d5..00000000 --- a/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Tests.Api.Auth.RootLogin.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj b/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin.sln b/Api.Auth.RootLogin/Api.Auth.RootLogin.sln deleted file mode 100644 index 74873446..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Auth.RootLogin.Models", "Api.Auth.RootLogin.Models\Api.Auth.RootLogin.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Auth.RootLogin", "Api.Auth.RootLogin\Api.Auth.RootLogin.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Auth.RootLogin", ".tests\Tests.Api.Auth.RootLogin\Tests.Api.Auth.RootLogin.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Api.Auth.RootLogin.csproj b/Api.Auth.RootLogin/Api.Auth.RootLogin/Api.Auth.RootLogin.csproj deleted file mode 100644 index f3a63dc6..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/Api.Auth.RootLogin.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/AuthController.cs b/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/AuthController.cs deleted file mode 100644 index 67904398..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/AuthController.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Api.Mvc.Authentication.Abstractions; - -namespace Api.Auth.RootLogin.Controllers; - -/// -public class AuthController(ILogger logger, IAuthRepository authRepository) - : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/ExamplesController.cs b/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/ExamplesController.cs deleted file mode 100644 index 3d9a508d..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Auth.RootLogin.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Authenticated Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("authenticated")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task AuthenticatedAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("authenticated"); - } -} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local b/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local deleted file mode 100644 index d87906a5..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.dll"] \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Program.cs b/Api.Auth.RootLogin/Api.Auth.RootLogin/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Properties/InternalsVisibleTo.cs b/Api.Auth.RootLogin/Api.Auth.RootLogin/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 540319fc..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Auth.RootLogin")] \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Development.json b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Development.json deleted file mode 100644 index 35df44e4..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Development.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", - "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV", - "Expiration": "24:00:00", - "RootLogin": { - "Username": "admin@domain.com", - "Password": "abc12|+d34DadD" - } - } - } - } -} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Production.json b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Staging.json b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.json b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.json deleted file mode 100644 index 1af6db64..00000000 --- a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Documentation": { - }, - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - } - } -} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Dockerfile b/Api.Auth.RootLogin/Dockerfile deleted file mode 100644 index 3330579b..00000000 --- a/Api.Auth.RootLogin/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.dll"] \ No newline at end of file diff --git a/Api.Auth.RootLogin/LICENSE b/Api.Auth.RootLogin/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Auth.RootLogin/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Auth.RootLogin/README.md b/Api.Auth.RootLogin/README.md deleted file mode 100644 index d750879d..00000000 --- a/Api.Auth.RootLogin/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# Api.Auth.RootLogin - -> _Nano API application with root login authentication._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#summary) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a derived `AuthController` as well as a simple test controller -that inherits from the top-level Nano `BaseController`. - -The JWT authentication scheme has been configured. Simply invoke the root login endpoint and use the returned JWT token in the `Authorization` header to authenticate when calling -the example controller endpoint. - -API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration -are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -The following endpoint from the auth controller is available for testing. - -| Endpoint | Description | -| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/auth/login/root` | Logins with the root credentials from configuration, and returns a simple `200 OK` response with a JWT token. | - -Additionally, the following endpoint is available for testing authorization. - -| Endpoint | Description | -| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/authenticate` | Returns a simple `200 OK` response, when JWT authorization is successful, and otherwise a `401 Unauthorized`. | - -> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#authentication)**. - -## Configuration -Configured the application with the necessary authentication setup. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - } -} -``` - -...and `appsettings.Development.json`. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", - "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", - "Expiration": "24:00:00", - "RootLogin": { - "Username": "admin@domain.com", - "Password": "abc12|+d34DadD" - } - } - } -} -``` - -## Kubernetes -For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be -enabled. Below demonstrates how to map the secret containing the JWT keys. - -```yaml -spec: - template: - spec: - containers: - env: - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key -``` - -## GitHub Action -The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. - -```yaml -env: - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} -``` - -...and created during the Kubernetes deploy step. - -```yaml -sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Api.Auth.RootLogin/icon.png b/Api.Auth.RootLogin/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Authorization/.docker/docker-compose.yml b/Api.Authorization/.docker/docker-compose.yml deleted file mode 100644 index b0511a39..00000000 --- a/Api.Authorization/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.authorization: - image: api.authorization - hostname: api-authorization - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Authorization - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Authorization/.dockerignore b/Api.Authorization/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Authorization/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Authorization/.github/config/slack.yml b/Api.Authorization/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Authorization/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Authorization/.github/workflows/build-and-deploy.yml b/Api.Authorization/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 91d8c254..00000000 --- a/Api.Authorization/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,179 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Authorization - IMAGE_NAME: api.authorization - SERVICE_NAME: api-authorization - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Authorization/.gitignore b/Api.Authorization/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Authorization/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Authorization/.kubernetes/autoscaler.yaml b/Api.Authorization/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Authorization/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Authorization/.kubernetes/configmap.yaml b/Api.Authorization/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Authorization/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Authorization/.kubernetes/deployment.yaml b/Api.Authorization/.kubernetes/deployment.yaml deleted file mode 100644 index 5041e227..00000000 --- a/Api.Authorization/.kubernetes/deployment.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Authorization/.kubernetes/service.yaml b/Api.Authorization/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Authorization/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Authorization/.tests/Tests.Api.Authorization/Properties/DoNotParallelize.cs b/Api.Authorization/.tests/Tests.Api.Authorization/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Authorization/.tests/Tests.Api.Authorization/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Authorization/.tests/Tests.Api.Authorization/Tests.Api.Authorization.csproj b/Api.Authorization/.tests/Tests.Api.Authorization/Tests.Api.Authorization.csproj deleted file mode 100644 index b96cdb94..00000000 --- a/Api.Authorization/.tests/Tests.Api.Authorization/Tests.Api.Authorization.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj b/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization.sln b/Api.Authorization/Api.Authorization.sln deleted file mode 100644 index 2f798b65..00000000 --- a/Api.Authorization/Api.Authorization.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Authorization.Models", "Api.Authorization.Models\Api.Authorization.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Authorization", "Api.Authorization\Api.Authorization.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Authorization", ".tests\Tests.Api.Authorization\Tests.Api.Authorization.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Authorization/Api.Authorization/Api.Authorization.csproj b/Api.Authorization/Api.Authorization/Api.Authorization.csproj deleted file mode 100644 index eb98bdda..00000000 --- a/Api.Authorization/Api.Authorization/Api.Authorization.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Controllers/AuthController.cs b/Api.Authorization/Api.Authorization/Controllers/AuthController.cs deleted file mode 100644 index 612d7030..00000000 --- a/Api.Authorization/Api.Authorization/Controllers/AuthController.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Api.Mvc.Authentication.Abstractions; - -namespace Api.Authorization.Controllers; - -/// -public class AuthController(ILogger logger, IAuthRepository authRepository) - : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Controllers/ExamplesController.cs b/Api.Authorization/Api.Authorization/Controllers/ExamplesController.cs deleted file mode 100644 index 9c055a05..00000000 --- a/Api.Authorization/Api.Authorization/Controllers/ExamplesController.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Authorization.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Authenticated Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("authenticated")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task AuthenticatedAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("authenticated"); - } - - /// - /// Forbidden Action. - /// - /// The cancellation token. - /// A message. - /// Unauthorized. - [HttpGet] - [Route("forbidden")] - [Authorize(Policy = "CustomPolicy")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task ForbiddenAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("forbidden"); - } -} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Dockerfile.Local b/Api.Authorization/Api.Authorization/Dockerfile.Local deleted file mode 100644 index bd2f4fec..00000000 --- a/Api.Authorization/Api.Authorization/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Authorization.dll"] \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Extensions/ServiceCollectionExtensions.cs b/Api.Authorization/Api.Authorization/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index 9bb73e85..00000000 --- a/Api.Authorization/Api.Authorization/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; - -namespace Api.Authorization.Extensions; - -internal static class ServiceCollectionExtensions -{ - internal static IServiceCollection AddCustomAuthorizationPolicy(this IServiceCollection services) - { - ArgumentNullException.ThrowIfNull(services); - - services - .AddAuthorization(x => - { - x.AddPolicy("CustomPolicy", y => y.RequireClaim("CustomClaim")); - }); - - return services; - } -} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Program.cs b/Api.Authorization/Api.Authorization/Program.cs deleted file mode 100644 index 6a29bda3..00000000 --- a/Api.Authorization/Api.Authorization/Program.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Api.Authorization.Extensions; -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddCustomAuthorizationPolicy(); - }) - .Build() - .Run(); diff --git a/Api.Authorization/Api.Authorization/Properties/InternalsVisibleTo.cs b/Api.Authorization/Api.Authorization/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 49bb94fb..00000000 --- a/Api.Authorization/Api.Authorization/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Authorization")] \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/appsettings.Development.json b/Api.Authorization/Api.Authorization/appsettings.Development.json deleted file mode 100644 index 3ecf604f..00000000 --- a/Api.Authorization/Api.Authorization/appsettings.Development.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", - "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV", - "Expiration": "24:00:00", - "RootLogin": { - "Username": "admin@domain.com", - "Password": "abc12|+d34DadD" - } - } - } - } -} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/appsettings.Production.json b/Api.Authorization/Api.Authorization/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Authorization/Api.Authorization/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/appsettings.Staging.json b/Api.Authorization/Api.Authorization/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Authorization/Api.Authorization/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/appsettings.json b/Api.Authorization/Api.Authorization/appsettings.json deleted file mode 100644 index 1af6db64..00000000 --- a/Api.Authorization/Api.Authorization/appsettings.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Documentation": { - }, - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - } - } -} \ No newline at end of file diff --git a/Api.Authorization/Dockerfile b/Api.Authorization/Dockerfile deleted file mode 100644 index 3a70d191..00000000 --- a/Api.Authorization/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Authorization.dll"] \ No newline at end of file diff --git a/Api.Authorization/LICENSE b/Api.Authorization/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Authorization/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Authorization/README.md b/Api.Authorization/README.md deleted file mode 100644 index 1fceab65..00000000 --- a/Api.Authorization/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Api.Authorization - -> _Nano API application with custom authorization policy._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Auth.RootLogin](https://github.com/Nano-Core/Nano.Lessons/tree/master/Auth.RootLogin)**. - -A custom authorization policy has been configured for the application. The `ServiceCollectionExtensions.AddCustomAuthorizationPolicy(...)` method adds the `CustomPolicy`, -and the `/forbidden` endpoint enforces the policy using a custom `[Authorize(Policy = "CustomPolicy")]` annotation. The policy itself requires the JWT token to contain -a `CustomClaim` claim type. - -API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration -are automatically excluded. In this example, only the root login action is exposed. - -API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration -are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -The following endpoint from the auth controller is available for testing. - -| Endpoint | Description | -| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/auth/login/root` | Logins with the root credentials from configuration, and returns a simple `200 OK` response with a JWT token. | - -Additionally, the following endpoint is available for testing authorization. - -| Endpoint | Description | -| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/authenticate` | Returns a simple `200 OK` response, when JWT authorization is successful. | -| `http://localhost:8080/api/examples/forbidden` | Returns a simple `200 OK` response, when JWT authorization is successful with `CustomClaim`, otherwise returns `403 FORBIDDEN` response. | - -> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#authentication)**. diff --git a/Api.Authorization/icon.png b/Api.Authorization/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.ContentNegotiation/.docker/docker-compose.yml b/Api.ContentNegotiation/.docker/docker-compose.yml deleted file mode 100644 index 31e6a9a3..00000000 --- a/Api.ContentNegotiation/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.contentnegotiation: - image: api.contentnegotiation - hostname: api-contentnegotiation - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.ContentNegotiation - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.ContentNegotiation/.dockerignore b/Api.ContentNegotiation/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.ContentNegotiation/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.ContentNegotiation/.github/config/slack.yml b/Api.ContentNegotiation/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.ContentNegotiation/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml b/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 4420d630..00000000 --- a/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.ContentNegotiation - IMAGE_NAME: api.contentnegotiation - SERVICE_NAME: api-contentnegotiation - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ContentNegotiation/.gitignore b/Api.ContentNegotiation/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.ContentNegotiation/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ContentNegotiation/.kubernetes/autoscaler.yaml b/Api.ContentNegotiation/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.ContentNegotiation/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ContentNegotiation/.kubernetes/configmap.yaml b/Api.ContentNegotiation/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.ContentNegotiation/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ContentNegotiation/.kubernetes/deployment.yaml b/Api.ContentNegotiation/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.ContentNegotiation/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ContentNegotiation/.kubernetes/service.yaml b/Api.ContentNegotiation/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.ContentNegotiation/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Properties/DoNotParallelize.cs b/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Tests.Api.ContentNegotiation.csproj b/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Tests.Api.ContentNegotiation.csproj deleted file mode 100644 index 101e0cd7..00000000 --- a/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Tests.Api.ContentNegotiation.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj b/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation.sln b/Api.ContentNegotiation/Api.ContentNegotiation.sln deleted file mode 100644 index 611424a3..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ContentNegotiation.Models", "Api.ContentNegotiation.Models\Api.ContentNegotiation.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ContentNegotiation", "Api.ContentNegotiation\Api.ContentNegotiation.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ContentNegotiation", ".tests\Tests.Api.ContentNegotiation\Tests.Api.ContentNegotiation.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Api.ContentNegotiation.csproj b/Api.ContentNegotiation/Api.ContentNegotiation/Api.ContentNegotiation.csproj deleted file mode 100644 index 99b477a7..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/Api.ContentNegotiation.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Controllers/ExamplesController.cs b/Api.ContentNegotiation/Api.ContentNegotiation/Controllers/ExamplesController.cs deleted file mode 100644 index c34fc3e4..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.ContentNegotiation.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Content Negotiation Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("content-negotiation")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task ContentNegotiationAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("content-negotiation"); - } -} \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local b/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local deleted file mode 100644 index dc9daf88..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ContentNegotiation.dll"] \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Program.cs b/Api.ContentNegotiation/Api.ContentNegotiation/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Properties/InternalsVisibleTo.cs b/Api.ContentNegotiation/Api.ContentNegotiation/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 27e3628a..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ContentNegotiation")] \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Development.json b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Production.json b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Staging.json b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.json b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.ContentNegotiation/Dockerfile b/Api.ContentNegotiation/Dockerfile deleted file mode 100644 index 658250a4..00000000 --- a/Api.ContentNegotiation/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.ContentNegotiation.dll"] \ No newline at end of file diff --git a/Api.ContentNegotiation/LICENSE b/Api.ContentNegotiation/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.ContentNegotiation/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.ContentNegotiation/README.md b/Api.ContentNegotiation/README.md deleted file mode 100644 index 1d3972f9..00000000 --- a/Api.ContentNegotiation/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Api.ContentNegotiation - -> _Nano API application with content negotiation._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example demonstrates how Nano handles a missing `Accept` header by defaulting the response format to JSON. - -The following endpoint is available for testing: - -| Endpoint | Description | -| --------------------------------------------------------- | --------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/content-negotiation` | Returns a simple `200 OK` response, no matter if `Accept` header is set or not. | - -> 📖 Learn more about **[Nano Content Negotiation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#content-negotiation)**. diff --git a/Api.ContentNegotiation/icon.png b/Api.ContentNegotiation/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Cookies/.docker/docker-compose.yml b/Api.Cookies/.docker/docker-compose.yml deleted file mode 100644 index 2ad288b3..00000000 --- a/Api.Cookies/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.cookies: - image: api.cookies - hostname: api-cookies - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Cookies - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Cookies/.dockerignore b/Api.Cookies/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Cookies/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Cookies/.github/config/slack.yml b/Api.Cookies/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Cookies/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Cookies/.github/workflows/build-and-deploy.yml b/Api.Cookies/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 8574604b..00000000 --- a/Api.Cookies/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Cookies - IMAGE_NAME: api.cookies - SERVICE_NAME: api-cookies - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Cookies/.gitignore b/Api.Cookies/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Cookies/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Cookies/.kubernetes/autoscaler.yaml b/Api.Cookies/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Cookies/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Cookies/.kubernetes/configmap.yaml b/Api.Cookies/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Cookies/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Cookies/.kubernetes/deployment.yaml b/Api.Cookies/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Cookies/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Cookies/.kubernetes/service.yaml b/Api.Cookies/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Cookies/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Cookies/.tests/Tests.Api.Cookies/Properties/DoNotParallelize.cs b/Api.Cookies/.tests/Tests.Api.Cookies/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Cookies/.tests/Tests.Api.Cookies/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Cookies/.tests/Tests.Api.Cookies/Tests.Api.Cookies.csproj b/Api.Cookies/.tests/Tests.Api.Cookies/Tests.Api.Cookies.csproj deleted file mode 100644 index 1cddb1ef..00000000 --- a/Api.Cookies/.tests/Tests.Api.Cookies/Tests.Api.Cookies.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj b/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies.sln b/Api.Cookies/Api.Cookies.sln deleted file mode 100644 index 4ed41ddd..00000000 --- a/Api.Cookies/Api.Cookies.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Cookies.Models", "Api.Cookies.Models\Api.Cookies.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Cookies", "Api.Cookies\Api.Cookies.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Cookies", ".tests\Tests.Api.Cookies\Tests.Api.Cookies.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Cookies/Api.Cookies/Api.Cookies.csproj b/Api.Cookies/Api.Cookies/Api.Cookies.csproj deleted file mode 100644 index 6ac27bd3..00000000 --- a/Api.Cookies/Api.Cookies/Api.Cookies.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/Controllers/ExamplesController.cs b/Api.Cookies/Api.Cookies/Controllers/ExamplesController.cs deleted file mode 100644 index 804fa030..00000000 --- a/Api.Cookies/Api.Cookies/Controllers/ExamplesController.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Cookies.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - private const string COOKIE_KEY = "TestKey"; - - /// - /// Set Cookie Action. - /// - /// The value to set in cookie. - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("set-cookie")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task SetCookieAsync([FromQuery][Required] string value, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - var options = new CookieOptions - { - HttpOnly = false, - Secure = true, - SameSite = SameSiteMode.Strict, - Expires = DateTimeOffset.UtcNow.AddMinutes(30) - }; - - this.Response.Cookies - .Append(COOKIE_KEY, value, options); - - return this.Ok("Cookie set"); - } - - /// - /// Get Cookie Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("get-cookie")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task GetCookie(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - var success = this.Request.Cookies - .TryGetValue(COOKIE_KEY, out var value); - - if (success) - { - return Ok($"Cookie value: {value}"); - } - - return Ok("cookie is empty or expired"); - } - - /// - /// Delete Cookie Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("delete-cookie")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task DeleteCookie(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - this.Response.Cookies - .Delete(COOKIE_KEY); - - return Ok("Cookie deleted"); - } -} \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/Dockerfile.Local b/Api.Cookies/Api.Cookies/Dockerfile.Local deleted file mode 100644 index 09cc9dd0..00000000 --- a/Api.Cookies/Api.Cookies/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Cookies.dll"] \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/Program.cs b/Api.Cookies/Api.Cookies/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Cookies/Api.Cookies/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Cookies/Api.Cookies/Properties/InternalsVisibleTo.cs b/Api.Cookies/Api.Cookies/Properties/InternalsVisibleTo.cs deleted file mode 100644 index fbbf81d6..00000000 --- a/Api.Cookies/Api.Cookies/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Cookies")] \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/appsettings.Development.json b/Api.Cookies/Api.Cookies/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Cookies/Api.Cookies/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/appsettings.Production.json b/Api.Cookies/Api.Cookies/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Cookies/Api.Cookies/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/appsettings.Staging.json b/Api.Cookies/Api.Cookies/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Cookies/Api.Cookies/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/appsettings.json b/Api.Cookies/Api.Cookies/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.Cookies/Api.Cookies/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.Cookies/Dockerfile b/Api.Cookies/Dockerfile deleted file mode 100644 index 2f4f37d9..00000000 --- a/Api.Cookies/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Cookies.dll"] \ No newline at end of file diff --git a/Api.Cookies/LICENSE b/Api.Cookies/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Cookies/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Cookies/README.md b/Api.Cookies/README.md deleted file mode 100644 index b2787c1b..00000000 --- a/Api.Cookies/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Api.Cookies - -> _Nano API application with cookies._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example demonstrates the use of cookies in a Nano application. - -The following endpoint is available for testing: - -| Endpoint | Description | -| -------------------------------------------------- | --------------------------------------------- | -| `http://localhost:8080/api/examples/set-cookie` | Sets a cookie returns a `200 OK`. | -| `http://localhost:8080/api/examples/get-cookie` | Gets a cookie if set and returns a `200 OK`. | -| `http://localhost:8080/api/examples/delete-cookie` | Deletes the cookie and returns a `200 OK`. | - -> 📖 Learn more about **[Nano Cookies](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Apid#cookies)**. - diff --git a/Api.Cookies/icon.png b/Api.Cookies/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.CustomConfigSection/.docker/docker-compose.yml b/Api.CustomConfigSection/.docker/docker-compose.yml deleted file mode 100644 index 9c4a6198..00000000 --- a/Api.CustomConfigSection/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.customconfigsection: - image: api.customconfigsection - hostname: api-customconfigsection - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.CustomConfigSection - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.CustomConfigSection/.dockerignore b/Api.CustomConfigSection/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.CustomConfigSection/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.CustomConfigSection/.github/config/slack.yml b/Api.CustomConfigSection/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.CustomConfigSection/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml b/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 007e17b7..00000000 --- a/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.CustomConfigSection - IMAGE_NAME: api.customconfigsection - SERVICE_NAME: api-customconfigsection - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.CustomConfigSection/.gitignore b/Api.CustomConfigSection/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.CustomConfigSection/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.CustomConfigSection/.kubernetes/autoscaler.yaml b/Api.CustomConfigSection/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.CustomConfigSection/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.CustomConfigSection/.kubernetes/configmap.yaml b/Api.CustomConfigSection/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.CustomConfigSection/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.CustomConfigSection/.kubernetes/deployment.yaml b/Api.CustomConfigSection/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.CustomConfigSection/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.CustomConfigSection/.kubernetes/service.yaml b/Api.CustomConfigSection/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.CustomConfigSection/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Properties/DoNotParallelize.cs b/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Tests.Api.CustomConfigSection.csproj b/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Tests.Api.CustomConfigSection.csproj deleted file mode 100644 index 1174e01e..00000000 --- a/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Tests.Api.CustomConfigSection.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj b/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection.sln b/Api.CustomConfigSection/Api.CustomConfigSection.sln deleted file mode 100644 index 05a5c0e0..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomConfigSection.Models", "Api.CustomConfigSection.Models\Api.CustomConfigSection.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomConfigSection", "Api.CustomConfigSection\Api.CustomConfigSection.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.CustomConfigSection", ".tests\Tests.Api.CustomConfigSection\Tests.Api.CustomConfigSection.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Api.CustomConfigSection.csproj b/Api.CustomConfigSection/Api.CustomConfigSection/Api.CustomConfigSection.csproj deleted file mode 100644 index 2863f706..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/Api.CustomConfigSection.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Config/CustomOptions.cs b/Api.CustomConfigSection/Api.CustomConfigSection/Config/CustomOptions.cs deleted file mode 100644 index 1321de75..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/Config/CustomOptions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Api.CustomConfigSection.Config; - -/// -/// Custom Options. -/// -public class CustomOptions -{ - internal static string SectionName => "Custom"; - - /// - /// Value. - /// - [Required] - public virtual string Value { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Controllers/ExamplesController.cs b/Api.CustomConfigSection/Api.CustomConfigSection/Controllers/ExamplesController.cs deleted file mode 100644 index 4c465b2e..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/Controllers/ExamplesController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.CustomConfigSection.Config; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Nano.App.Api.Controllers; - -namespace Api.CustomConfigSection.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IOptionsMonitor customOptions) : BaseController(logger) -{ - private readonly IOptionsMonitor customOptions = customOptions ?? throw new ArgumentNullException(nameof(customOptions)); - - /// - /// Custom Config Section Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("custom-config-section")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task CustomConfigSectionAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok($"Custom Config Section Value: '{this.customOptions.CurrentValue.Value}'"); - } -} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local b/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local deleted file mode 100644 index 96d8dc2e..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Program.cs b/Api.CustomConfigSection/Api.CustomConfigSection/Program.cs deleted file mode 100644 index eb79f126..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Api.CustomConfigSection.Config; -using Nano.App.Api; -using Nano.Common.Config.Extensions; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoConfigSection(CustomOptions.SectionName, out _); - }) - .Build() - .Run(); diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Properties/InternalsVisibleTo.cs b/Api.CustomConfigSection/Api.CustomConfigSection/Properties/InternalsVisibleTo.cs deleted file mode 100644 index a6d1e8ad..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.CustomConfigSection")] \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Development.json b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Production.json b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Staging.json b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.json b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.json deleted file mode 100644 index 33396aac..00000000 --- a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Custom": { - "Value": "custom-value" - } -} \ No newline at end of file diff --git a/Api.CustomConfigSection/Dockerfile b/Api.CustomConfigSection/Dockerfile deleted file mode 100644 index ee947826..00000000 --- a/Api.CustomConfigSection/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Api.CustomConfigSection/LICENSE b/Api.CustomConfigSection/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.CustomConfigSection/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.CustomConfigSection/README.md b/Api.CustomConfigSection/README.md deleted file mode 100644 index 74dcdc22..00000000 --- a/Api.CustomConfigSection/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Api.CustomConfigSection - -> _Nano API application with a custom configuration section._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Rememmber to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [Registration](#registration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example illustrates how custom configuration sections can be effortlessly registered within a Nano application. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ----------------------------------------------------------- | -------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/custom-config-section` | Returns a simple `200 OK` response with the custom configuration value. | - -> 📖 Learn more about **[Nano Custom Configuration Sections](https://github.com/Nano-Core/Nano.Library/Nano.App.Api/README.md#custom-configuration-section)**. - -## Configuration -A custom configuration section has been added to `appsettings.json`: - -```json -"Custom": { - "Value": "custom-value" -} -``` - -## Registration -An option class matching the structure of the configuration has been implemented. - -```csharp -public class CustomOptions -{ - internal static string SectionName => "Custom"; - - [Required] - public virtual string Value { get; set; } = null!; -} -``` - -Finally, the options model has been registered with the section in startup, as shown below. - -```csharp -... -.ConfigureServices(x => -{ - x.AddNanoConfigSection(CustomOptions.SectionName, out _); -}) -... -``` diff --git a/Api.CustomConfigSection/icon.png b/Api.CustomConfigSection/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.CustomMiddleware/.docker/docker-compose.yml b/Api.CustomMiddleware/.docker/docker-compose.yml deleted file mode 100644 index 21e01392..00000000 --- a/Api.CustomMiddleware/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.custommiddleware: - image: api.custommiddleware - hostname: api-custommiddleware - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.CustomMiddleware - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.CustomMiddleware/.dockerignore b/Api.CustomMiddleware/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.CustomMiddleware/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.CustomMiddleware/.github/config/slack.yml b/Api.CustomMiddleware/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.CustomMiddleware/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml b/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 30521710..00000000 --- a/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.CustomMiddleware - IMAGE_NAME: api.custommiddleware - SERVICE_NAME: api-custommiddleware - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.CustomMiddleware/.gitignore b/Api.CustomMiddleware/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.CustomMiddleware/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.CustomMiddleware/.kubernetes/autoscaler.yaml b/Api.CustomMiddleware/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.CustomMiddleware/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.CustomMiddleware/.kubernetes/configmap.yaml b/Api.CustomMiddleware/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.CustomMiddleware/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.CustomMiddleware/.kubernetes/deployment.yaml b/Api.CustomMiddleware/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.CustomMiddleware/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.CustomMiddleware/.kubernetes/service.yaml b/Api.CustomMiddleware/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.CustomMiddleware/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Properties/DoNotParallelize.cs b/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Tests.Api.CustomMiddleware.csproj b/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Tests.Api.CustomMiddleware.csproj deleted file mode 100644 index a47d09e0..00000000 --- a/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Tests.Api.CustomMiddleware.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj b/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware.sln b/Api.CustomMiddleware/Api.CustomMiddleware.sln deleted file mode 100644 index 0cdb2e95..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomMiddleware.Models", "Api.CustomMiddleware.Models\Api.CustomMiddleware.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomMiddleware", "Api.CustomMiddleware\Api.CustomMiddleware.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.CustomMiddleware", ".tests\Tests.Api.CustomMiddleware\Tests.Api.CustomMiddleware.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Api.CustomMiddleware.csproj b/Api.CustomMiddleware/Api.CustomMiddleware/Api.CustomMiddleware.csproj deleted file mode 100644 index 05602204..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/Api.CustomMiddleware.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Controllers/ExamplesController.cs b/Api.CustomMiddleware/Api.CustomMiddleware/Controllers/ExamplesController.cs deleted file mode 100644 index 4f1ebd38..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.CustomMiddleware.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Custom Middleware Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("custom-middleware")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task CustomMiddlewareAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("custom-middleware"); - } -} \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local b/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local deleted file mode 100644 index e059926f..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.CustomMiddleware.dll"] \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Program.cs b/Api.CustomMiddleware/Api.CustomMiddleware/Program.cs deleted file mode 100644 index e96b39e7..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Microsoft.AspNetCore.Builder; -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build(x => - { - x.Use((context, next) => - { - context.Response.Headers["CustomMiddleware"] = "awesome"; - - return next(); - }); - }) - .Run(); \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Properties/InternalsVisibleTo.cs b/Api.CustomMiddleware/Api.CustomMiddleware/Properties/InternalsVisibleTo.cs deleted file mode 100644 index b62f0409..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.CustomMiddleware")] \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Development.json b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Production.json b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Staging.json b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.json b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.CustomMiddleware/Dockerfile b/Api.CustomMiddleware/Dockerfile deleted file mode 100644 index a5772da5..00000000 --- a/Api.CustomMiddleware/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.CustomMiddleware.dll"] \ No newline at end of file diff --git a/Api.CustomMiddleware/LICENSE b/Api.CustomMiddleware/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.CustomMiddleware/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.CustomMiddleware/README.md b/Api.CustomMiddleware/README.md deleted file mode 100644 index ac0aac9d..00000000 --- a/Api.CustomMiddleware/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Api.CustomMiddleware - -> _Nano API application with http._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example illustrates how custom middleware can be registered within a Nano application. - -The following endpoint is available for testing: - -| Endpoint | Description | -| ------------------------------------------------------- | ------------------------------------------------------------ | -| `http://localhost:8080/api/examples/custom-middleware` | Returns a simple `200 OK` response, with the custom header. | - -> 📖 Learn more about **[Nano Custom Middleware](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#custom-middleware)**. - -## Registration -The application register custom middleware that adds a header `CustomMiddleware` to all response with the value `awesome`, as shown below. - -```csharp -... -.Build(builder => -{ - builder - .Use((context, next) => - { - context.Response.Headers["CustomMiddleware"] = "awesome"; - - return next(); - }); -}) -... -``` diff --git a/Api.CustomMiddleware/icon.png b/Api.CustomMiddleware/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.CustomService/.docker/docker-compose.yml b/Api.CustomService/.docker/docker-compose.yml deleted file mode 100644 index 2b5bedb8..00000000 --- a/Api.CustomService/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.customservice: - image: api.customservice - hostname: api-customservice - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.CustomService - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.CustomService/.dockerignore b/Api.CustomService/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.CustomService/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.CustomService/.github/config/slack.yml b/Api.CustomService/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.CustomService/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.CustomService/.github/workflows/build-and-deploy.yml b/Api.CustomService/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 9956e837..00000000 --- a/Api.CustomService/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.CustomService - IMAGE_NAME: api.customservice - SERVICE_NAME: api-customservice - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.CustomService/.gitignore b/Api.CustomService/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.CustomService/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.CustomService/.kubernetes/autoscaler.yaml b/Api.CustomService/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.CustomService/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.CustomService/.kubernetes/configmap.yaml b/Api.CustomService/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.CustomService/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.CustomService/.kubernetes/deployment.yaml b/Api.CustomService/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.CustomService/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.CustomService/.kubernetes/service.yaml b/Api.CustomService/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.CustomService/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.CustomService/.tests/Tests.Api.CustomService/Properties/DoNotParallelize.cs b/Api.CustomService/.tests/Tests.Api.CustomService/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.CustomService/.tests/Tests.Api.CustomService/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.CustomService/.tests/Tests.Api.CustomService/Tests.Api.CustomService.csproj b/Api.CustomService/.tests/Tests.Api.CustomService/Tests.Api.CustomService.csproj deleted file mode 100644 index 2366fb2e..00000000 --- a/Api.CustomService/.tests/Tests.Api.CustomService/Tests.Api.CustomService.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj b/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService.sln b/Api.CustomService/Api.CustomService.sln deleted file mode 100644 index 18b03228..00000000 --- a/Api.CustomService/Api.CustomService.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomService.Models", "Api.CustomService.Models\Api.CustomService.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomService", "Api.CustomService\Api.CustomService.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.CustomService", ".tests\Tests.Api.CustomService\Tests.Api.CustomService.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.CustomService/Api.CustomService/Api.CustomService.csproj b/Api.CustomService/Api.CustomService/Api.CustomService.csproj deleted file mode 100644 index 25b46e86..00000000 --- a/Api.CustomService/Api.CustomService/Api.CustomService.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Controllers/ExamplesController.cs b/Api.CustomService/Api.CustomService/Controllers/ExamplesController.cs deleted file mode 100644 index 8497b956..00000000 --- a/Api.CustomService/Api.CustomService/Controllers/ExamplesController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.CustomService.Services.Abstractions; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.CustomService.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IExampleService exampleService) : BaseController(logger) -{ - private readonly IExampleService exampleService = exampleService ?? throw new ArgumentNullException(nameof(exampleService)); - - /// - /// Custom Service Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("custom-service")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task CustomServiceAsync(CancellationToken cancellationToken = default) - { - var message = await this.exampleService - .GetMessage(); - - return this.Ok($"custom-service message: {message}"); - } -} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Dockerfile.Local b/Api.CustomService/Api.CustomService/Dockerfile.Local deleted file mode 100644 index 83ac0c36..00000000 --- a/Api.CustomService/Api.CustomService/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.CustomService.dll"] \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Program.cs b/Api.CustomService/Api.CustomService/Program.cs deleted file mode 100644 index 07917592..00000000 --- a/Api.CustomService/Api.CustomService/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.CustomService.Services; -using Api.CustomService.Services.Abstractions; -using Microsoft.Extensions.DependencyInjection; -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddScoped(); - }) - .Build() - .Run(); diff --git a/Api.CustomService/Api.CustomService/Properties/InternalsVisibleTo.cs b/Api.CustomService/Api.CustomService/Properties/InternalsVisibleTo.cs deleted file mode 100644 index c8d30be6..00000000 --- a/Api.CustomService/Api.CustomService/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.CustomService")] \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Services/Abstractions/IExampleService.cs b/Api.CustomService/Api.CustomService/Services/Abstractions/IExampleService.cs deleted file mode 100644 index 1413cf76..00000000 --- a/Api.CustomService/Api.CustomService/Services/Abstractions/IExampleService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Threading.Tasks; - -namespace Api.CustomService.Services.Abstractions; - -/// -/// Example Service Interface. -/// -public interface IExampleService -{ - /// - /// Get Message. - /// - /// A message. - Task GetMessage(); -} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Services/ExampleService.cs b/Api.CustomService/Api.CustomService/Services/ExampleService.cs deleted file mode 100644 index a45d5c6d..00000000 --- a/Api.CustomService/Api.CustomService/Services/ExampleService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Api.CustomService.Services.Abstractions; -using System.Threading.Tasks; - -namespace Api.CustomService.Services; - -/// -/// Example Service. -/// -public class ExampleService : IExampleService -{ - /// - public Task GetMessage() - { - return Task.FromResult("Message from example service."); - } -} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/appsettings.Development.json b/Api.CustomService/Api.CustomService/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomService/Api.CustomService/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/appsettings.Production.json b/Api.CustomService/Api.CustomService/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomService/Api.CustomService/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/appsettings.Staging.json b/Api.CustomService/Api.CustomService/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.CustomService/Api.CustomService/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/appsettings.json b/Api.CustomService/Api.CustomService/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.CustomService/Api.CustomService/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.CustomService/Dockerfile b/Api.CustomService/Dockerfile deleted file mode 100644 index 8679c6c7..00000000 --- a/Api.CustomService/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.CustomService.dll"] \ No newline at end of file diff --git a/Api.CustomService/LICENSE b/Api.CustomService/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.CustomService/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.CustomService/README.md b/Api.CustomService/README.md deleted file mode 100644 index 929ae32a..00000000 --- a/Api.CustomService/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Api.CustomService - -> _Nano API application with custom scoped service._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example demonstrates how a custom service implementation can be registered and used within a Nano application. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ---------------------------------------------------- | -------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/custom-servuce` | Returns a simple `200 OK` response, with a message from the `IExampleServuce` | - -> 📖 Learn more about **[Nano Custom Services](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App#custom-services)**. - -## Registration -A custom service, `IExampleService` has been added and implemented. In `program.cs` the service is registered using `ConfigureService(...)` method as shown below - -```csharp -... -.ConfigureServices(services => -{ - services - .AddScoped(); -}) -... -``` diff --git a/Api.CustomService/icon.png b/Api.CustomService/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.Audit/.docker/docker-compose.yml b/Api.Data.Audit/.docker/docker-compose.yml deleted file mode 100644 index ee96146f..00000000 --- a/Api.Data.Audit/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.repository.audit: - image: api.data.repository.audit - hostname: api-data-audit - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.Audit - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.Audit/.dockerignore b/Api.Data.Audit/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.Audit/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.Audit/.github/config/slack.yml b/Api.Data.Audit/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.Audit/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Audit/.github/workflows/build-and-deploy.yml b/Api.Data.Audit/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 6b9f5e43..00000000 --- a/Api.Data.Audit/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.Audit - IMAGE_NAME: api.data.audit - SERVICE_NAME: api-data-audit - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Audit/.gitignore b/Api.Data.Audit/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.Audit/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Audit/.kubernetes/autoscaler.yaml b/Api.Data.Audit/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.Audit/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Audit/.kubernetes/configmap.yaml b/Api.Data.Audit/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.Audit/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Audit/.kubernetes/deployment.yaml b/Api.Data.Audit/.kubernetes/deployment.yaml deleted file mode 100644 index 5901ef66..00000000 --- a/Api.Data.Audit/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Audit/.kubernetes/service.yaml b/Api.Data.Audit/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.Audit/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Properties/DoNotParallelize.cs b/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Tests.Api.Data.Audit.csproj b/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Tests.Api.Data.Audit.csproj deleted file mode 100644 index 48e9ce75..00000000 --- a/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Tests.Api.Data.Audit.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj b/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.Audit/Api.Data.Audit.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index b98f0e56..00000000 --- a/Api.Data.Audit/Api.Data.Audit.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.Audit.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.Models/Example.cs b/Api.Data.Audit/Api.Data.Audit.Models/Example.cs deleted file mode 100644 index 349b116e..00000000 --- a/Api.Data.Audit/Api.Data.Audit.Models/Example.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Nano.Data.Abstractions.Annotations; -using Nano.Data.Abstractions.Models; -using Nano.Data.Abstractions.Models.Abstractions; - -namespace Api.Data.Audit.Models; - -/// -/// Example. -/// -public class Example : BaseEntity, IEntityAuditable, IEntitySoftDeletable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; - - /// - /// Navigation Id. - /// - public virtual Guid? NavigationId { get; set; } - - /// - /// Navigation. - /// - [Include] - public virtual ExampleNavigation? Navigation { get; set; } -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.Models/ExampleNavigation.cs b/Api.Data.Audit/Api.Data.Audit.Models/ExampleNavigation.cs deleted file mode 100644 index 021c4740..00000000 --- a/Api.Data.Audit/Api.Data.Audit.Models/ExampleNavigation.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using Nano.Data.Abstractions.Models; -using Nano.Data.Abstractions.Models.Abstractions; - -namespace Api.Data.Audit.Models; - -/// -/// Example Navigation. -/// -public class ExampleNavigation : BaseEntity, IEntityAuditable, IEntitySoftDeletable -{ - /// - /// Navigation Name. - /// - public virtual string NavigationName { get; set; } = null!; - - /// - /// Examples. - /// - public virtual ICollection Examples { get; set; } = []; -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.Models/ExampleNoAudit.cs b/Api.Data.Audit/Api.Data.Audit.Models/ExampleNoAudit.cs deleted file mode 100644 index 052ee8df..00000000 --- a/Api.Data.Audit/Api.Data.Audit.Models/ExampleNoAudit.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Audit.Models; - -/// -/// Example. -/// -public class ExampleNoAudit : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.sln b/Api.Data.Audit/Api.Data.Audit.sln deleted file mode 100644 index 36b38b53..00000000 --- a/Api.Data.Audit/Api.Data.Audit.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Audit.Models", "Api.Data.Audit.Models\Api.Data.Audit.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Audit", "Api.Data.Audit\Api.Data.Audit.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Audit", ".tests\Tests.Api.Data.Audit\Tests.Api.Data.Audit.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.Audit/Api.Data.Audit/Api.Data.Audit.csproj b/Api.Data.Audit/Api.Data.Audit/Api.Data.Audit.csproj deleted file mode 100644 index 644eec10..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Api.Data.Audit.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Controllers/AuditController.cs b/Api.Data.Audit/Api.Data.Audit/Controllers/AuditController.cs deleted file mode 100644 index 131c3a1d..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Controllers/AuditController.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.Audit.Controllers; - -/// -public class AuditController(ILogger logger, IRepository repository) - : BaseAuditController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Controllers/ExampleNoAuditsController.cs b/Api.Data.Audit/Api.Data.Audit/Controllers/ExampleNoAuditsController.cs deleted file mode 100644 index 40f20b8f..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Controllers/ExampleNoAuditsController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.Audit.Models; -using Api.Data.Audit.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.Audit.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleNoAuditsController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Controllers/ExamplesController.cs b/Api.Data.Audit/Api.Data.Audit/Controllers/ExamplesController.cs deleted file mode 100644 index 888ff808..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.Audit.Models; -using Api.Data.Audit.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.Audit.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleMapping.cs b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 8ad77cee..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using Api.Data.Audit.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; -using Nano.Data.Mappings.Extensions; - -namespace Api.Data.Audit.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasOne(x => x.Navigation) - .WithMany(x => x.Examples); - - builder - .OnUpdating(x => - { - x.Entity.Name += "-triggered"; - }); - } -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNavigationMapping.cs b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNavigationMapping.cs deleted file mode 100644 index 642f2122..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNavigationMapping.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Api.Data.Audit.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.Audit.Data.Mappings; - -/// -/// Example Navigation Mapping. -/// -public class ExampleNavigationMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.NavigationName); - - builder - .HasMany(x => x.Examples) - .WithOne(x => x.Navigation); - } -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNoAuditMapping.cs b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNoAuditMapping.cs deleted file mode 100644 index 54ad4ad7..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNoAuditMapping.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Api.Data.Audit.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.Audit.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleNoAuditMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContext.cs b/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContext.cs deleted file mode 100644 index 1781e44e..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.Audit.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContextFactory.cs b/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContextFactory.cs deleted file mode 100644 index aeb1d1ec..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.Audit.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local b/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local deleted file mode 100644 index 02405ae6..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.Audit.dll"] \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.Designer.cs b/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.Designer.cs deleted file mode 100644 index 52f7cfd4..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.Designer.cs +++ /dev/null @@ -1,728 +0,0 @@ -// -using System; -using Api.Data.Audit.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Audit.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415133755_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Audit.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("NavigationId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("NavigationId"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("NavigationName") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleNavigation"); - }); - - modelBuilder.Entity("Api.Data.Audit.Models.ExampleNoAudit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleNoAudit"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Audit.Models.Example", b => - { - b.HasOne("Api.Data.Audit.Models.ExampleNavigation", "Navigation") - .WithMany("Examples") - .HasForeignKey("NavigationId"); - - b.Navigation("Navigation"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => - { - b.Navigation("Examples"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.cs b/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.cs deleted file mode 100644 index dade123c..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.cs +++ /dev/null @@ -1,667 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.Audit.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleNavigation", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NavigationName = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleNavigation", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleNoAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleNoAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - NavigationId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - table.ForeignKey( - name: "FK_Example_ExampleNavigation_NavigationId", - column: x => x.NavigationId, - principalTable: "ExampleNavigation", - principalColumn: "Id"); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_NavigationId", - table: "Example", - column: "NavigationId"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleNavigation_CreatedAt", - table: "ExampleNavigation", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleNavigation_IsDeleted", - table: "ExampleNavigation", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleNoAudit_CreatedAt", - table: "ExampleNoAudit", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleNoAudit_IsDeleted", - table: "ExampleNoAudit", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "ExampleNoAudit"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "ExampleNavigation"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.Audit/Api.Data.Audit/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Audit/Api.Data.Audit/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 1cdb7577..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,725 +0,0 @@ -// -using System; -using Api.Data.Audit.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Audit.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Audit.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("NavigationId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("NavigationId"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("NavigationName") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleNavigation"); - }); - - modelBuilder.Entity("Api.Data.Audit.Models.ExampleNoAudit", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleNoAudit"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Audit.Models.Example", b => - { - b.HasOne("Api.Data.Audit.Models.ExampleNavigation", "Navigation") - .WithMany("Examples") - .HasForeignKey("NavigationId"); - - b.Navigation("Navigation"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => - { - b.Navigation("Examples"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Audit/Api.Data.Audit/Program.cs b/Api.Data.Audit/Api.Data.Audit/Program.cs deleted file mode 100644 index 9bf00ea0..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.Audit.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.Audit/Api.Data.Audit/Properties/InternalsVisibleTo.cs b/Api.Data.Audit/Api.Data.Audit/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 44f0ccde..00000000 --- a/Api.Data.Audit/Api.Data.Audit/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.Audit")] \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json b/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.Production.json b/Api.Data.Audit/Api.Data.Audit/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Audit/Api.Data.Audit/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.Staging.json b/Api.Data.Audit/Api.Data.Audit/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Audit/Api.Data.Audit/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.json b/Api.Data.Audit/Api.Data.Audit/appsettings.json deleted file mode 100644 index 4439ec87..00000000 --- a/Api.Data.Audit/Api.Data.Audit/appsettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Documentation": { - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.Audit/Dockerfile b/Api.Data.Audit/Dockerfile deleted file mode 100644 index d8e60daa..00000000 --- a/Api.Data.Audit/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.Audit.dll"] \ No newline at end of file diff --git a/Api.Data.Audit/LICENSE b/Api.Data.Audit/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.Audit/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Audit/README.md b/Api.Data.Audit/README.md deleted file mode 100644 index e33093b3..00000000 --- a/Api.Data.Audit/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Api.Data.Audit - -> _Nano API application with data audit logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to -demonstrate audit logging. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. Also an `AuditControlller` derived from -`BaseAuditControlller` has been added. - -The `Example` entity implements `IEntityAuditable`. The entity model is also mapped with a `OnUpdating(...)` trigger to prove that changes to the entity model will be reflected -in audit properties. Last, the entity model also implements the `IEntitySoftDeletable` so when deleted the `AuditEntry.State` will be `SoftDeleted`. - -The `AuditController`, which derives from the base `BaseAuditController` in Nano, exposes read-only endpoints for the `AuditEntry` entity. When retrieving or querying -audit entries, the related `AuditEntryProperties` are automatically included, ensuring that all relevant details are available without additional queries. - -Notice, that when you set the `X-Request-Id` header, the value is automatically recorded in the audit entry. Also the `CreatedBy` is set, and in this case `Anonymous` because -we are invoking the endpoint with an unauthenticated user. - -Also, API documentation has been configured, in order to easier see which audit endpoints are available. It can be accessed -here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -> 📖 Learn more about **[Nano Data Audit](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#audit)**. diff --git a/Api.Data.Audit/icon.png b/Api.Data.Audit/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.EntityEvents/.docker/docker-compose.yml b/Api.Data.EntityEvents/.docker/docker-compose.yml deleted file mode 100644 index c7513f0a..00000000 --- a/Api.Data.EntityEvents/.docker/docker-compose.yml +++ /dev/null @@ -1,62 +0,0 @@ -services: - api.data.entityevents: - image: api.data.entityevents - hostname: api-data-entityevents - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.EntityEvents - dockerfile: "Dockerfile.Local" - depends_on: - - database - - eventing - networks: - - network - - api.data.entityevents.subscriber: - image: api.data.entityevents.subscriber - hostname: api-data-entityevents.subscriber - restart: on-failure - ports: - - 8181:8181 - build: - context: ../Api.Data.EntityEvents.Subscriber - dockerfile: "Dockerfile.Local" - depends_on: - - database - - eventing - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_ROOT_HOST: '%' - volumes: - - ./init.sql:/docker-entrypoint-initdb.d/init.sql - - eventing: - image: rabbitmq:management - hostname: rabbitmq - ports: - - 5671:5671 - - 5672:5672 - - 15671:15671 - - 15672:15672 - networks: - - network - environment: - RABBITMQ_DEFAULT_USER: rabbitmq_user - RABBITMQ_DEFAULT_PASS: password - RABBITMQ_DEFAULT_VHOST: / - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.EntityEvents/.docker/init.sql b/Api.Data.EntityEvents/.docker/init.sql deleted file mode 100644 index 872a2eda..00000000 --- a/Api.Data.EntityEvents/.docker/init.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE DATABASE IF NOT EXISTS nanoDb; -CREATE DATABASE IF NOT EXISTS nanoDb2; - -CREATE USER 'sa'@'%' IDENTIFIED BY 'myPassword_123'; - -GRANT ALL PRIVILEGES ON nanoDb.* TO 'sa'@'%'; -GRANT ALL PRIVILEGES ON nanoDb2.* TO 'sa'@'%'; - -FLUSH PRIVILEGES; \ No newline at end of file diff --git a/Api.Data.EntityEvents/.dockerignore b/Api.Data.EntityEvents/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.EntityEvents/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.EntityEvents/.github/config/slack.yml b/Api.Data.EntityEvents/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.EntityEvents/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml b/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 9377aa15..00000000 --- a/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.EntityEvents - IMAGE_NAME: api.data.entityevents - SERVICE_NAME: api-data-entityevents - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.EntityEvents/.gitignore b/Api.Data.EntityEvents/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.EntityEvents/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.EntityEvents/.kubernetes/autoscaler.yaml b/Api.Data.EntityEvents/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.EntityEvents/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.EntityEvents/.kubernetes/configmap.yaml b/Api.Data.EntityEvents/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.EntityEvents/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.EntityEvents/.kubernetes/deployment.yaml b/Api.Data.EntityEvents/.kubernetes/deployment.yaml deleted file mode 100644 index c54d54cc..00000000 --- a/Api.Data.EntityEvents/.kubernetes/deployment.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - - name: Eventing__Credentials__Secret - valueFrom: - secretKeyRef: - name: rabbitmq - key: rabbitmq-password -``` envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.EntityEvents/.kubernetes/service.yaml b/Api.Data.EntityEvents/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.EntityEvents/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Properties/DoNotParallelize.cs b/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Tests.Api.Data.EntityEvents.csproj b/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Tests.Api.Data.EntityEvents.csproj deleted file mode 100644 index 8015ac15..00000000 --- a/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Tests.Api.Data.EntityEvents.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Address.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Address.cs deleted file mode 100644 index 584d7745..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Address.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Nano.Data.Abstractions.Models; -using System.ComponentModel.DataAnnotations; - -namespace Api.Data.EntityEvents.Models; - -/// -/// Address. -/// -public class Address : BaseEntity -{ - /// - /// Street. - /// - [Required] - public virtual string Street { get; set; } = null!; - - /// - /// Profile. - /// - public virtual Profile? Profile { get; set; } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj deleted file mode 100644 index c58bf942..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj +++ /dev/null @@ -1,79 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Criterias/DefaultQueryCriteria.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Criterias/DefaultQueryCriteria.cs deleted file mode 100644 index f73873a8..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Criterias/DefaultQueryCriteria.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.EntityEvents.Models.Criterias; - -/// -public class DefaultQueryCriteria : BaseQueryCriteria -{ - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Customer.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Customer.cs deleted file mode 100644 index 50db7b25..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Customer.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Api.Data.EntityEvents.Models.Owned; -using Nano.Data.Abstractions.Annotations; -using Nano.Data.Abstractions.Eventing.Annotations; - -namespace Api.Data.EntityEvents.Models; - -/// -/// Customer. -/// -[Publish( - nameof(Name), - nameof(ProfileId), - $"{nameof(Profile)}.{nameof(Profile.AddressId)}", - $"{nameof(Profile)}.{nameof(Profile.Settings)}.{nameof(ProfileSettings.UseDarkMode)}", - $"{nameof(Profile)}.{nameof(Profile.Address)}.{nameof(Address.Street)}")] -public class Customer : Person -{ - /// - /// Name. - /// - [Required] - public virtual string Name { get; set; } = null!; - - /// - /// Profile Id. - /// - public virtual Guid? ProfileId { get; set; } - - /// - /// Profile. - /// - [Include] - public virtual Profile? Profile { get; set; } - - /// - /// Profile. - /// - [Required] - public virtual ICollection Orders { get; set; } = []; -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Order.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Order.cs deleted file mode 100644 index ac98f255..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Order.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.EntityEvents.Models; - -/// -/// Order. -/// -public class Order : BaseEntity -{ - /// - /// Customer Id. - /// - [Required] - public virtual Guid CustomerId { get; set; } - - /// - /// Customer. - /// - public virtual Customer? Customer { get; set; } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Owned/ProfileSettings.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Owned/ProfileSettings.cs deleted file mode 100644 index 63d43f20..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Owned/ProfileSettings.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Api.Data.EntityEvents.Models.Owned; - -/// -/// Profile Settings -/// -public class ProfileSettings -{ - /// - /// Use Dark Mode. - /// - [Required] - public virtual bool UseDarkMode { get; set; } = false; -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Person.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Person.cs deleted file mode 100644 index 924500bc..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Person.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Nano.Data.Abstractions.Eventing.Annotations; -using Nano.Data.Abstractions.Models; -using System.ComponentModel.DataAnnotations; - -namespace Api.Data.EntityEvents.Models; - -/// -/// Example Parent. -/// -[Publish(nameof(Identitifer))] -public class Person : BaseEntity -{ - /// - /// Name. - /// - [Required] - public virtual string Identitifer { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Profile.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Profile.cs deleted file mode 100644 index 7bf7e7c6..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Profile.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Nano.Data.Abstractions.Models; -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Api.Data.EntityEvents.Models.Owned; - -namespace Api.Data.EntityEvents.Models; - -/// -/// Customer Profile. -/// -public class Profile : BaseEntity -{ - /// - /// Address Id. - /// - public virtual Guid? AddressId { get; set; } - - /// - /// Address. - /// - public virtual Address? Address { get; set; } - - /// - /// Address. - /// - [Required] - public virtual ProfileSettings Settings { get; set; } = new(); - - /// - /// Customers. - /// - [Required] - public virtual ICollection Customers { get; set; } = []; -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj deleted file mode 100644 index c58bf942..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj +++ /dev/null @@ -1,79 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Customer.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Customer.cs deleted file mode 100644 index 5181e998..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Customer.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Eventing.Annotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.EntityEvents.Subscriber.Models; - -/// -/// Customer. -/// -[Subscribe] -public class Customer : BaseEntity -{ - /// - /// Name. - /// - [Required] - public virtual string Identitifer { get; set; } = null!; - - /// - /// Name. - /// - [Required] - public virtual string Name { get; set; } = null!; - - /// - /// Profile Id. - /// - [Required] - public virtual Guid ProfileId { get; set; } - - /// - /// Address Id. - /// - public virtual Guid? AddressId { get; set; } - - /// - /// Use Dark Mode. - /// - [Required] - public virtual bool UseDarkMode { get; set; } = false; - - /// - /// Street. - /// - public virtual string? Street { get; set; } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Api.Data.EntityEvents.Subscriber.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Api.Data.EntityEvents.Subscriber.csproj deleted file mode 100644 index d7d00c69..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Api.Data.EntityEvents.Subscriber.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/Mappings/CustomerMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/Mappings/CustomerMapping.cs deleted file mode 100644 index 080eaa31..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/Mappings/CustomerMapping.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using Api.Data.EntityEvents.Subscriber.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.EntityEvents.Subscriber.Data.Mappings; - -/// -/// Customer Mapping. -/// -public class CustomerMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Identitifer) - .IsRequired(); - - builder - .Property(x => x.Name) - .IsRequired(); - - builder - .Property(x => x.ProfileId); - - builder - .Property(x => x.AddressId); - - builder - .Property(x => x.UseDarkMode) - .IsRequired(); - - builder - .Property(x => x.Street); - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContext.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContext.cs deleted file mode 100644 index f2846958..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.EntityEvents.Subscriber.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContextFactory.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 12b01c22..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.EntityEvents.Subscriber.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local deleted file mode 100644 index 6d48bd26..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.EntityEvents.Subscriber.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.Designer.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.Designer.cs deleted file mode 100644 index d74e48dc..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.Designer.cs +++ /dev/null @@ -1,665 +0,0 @@ -// -using System; -using Api.Data.EntityEvents.Subscriber.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.EntityEvents.Subscriber.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260414100646_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.EntityEvents.Subscriber.Models.Customer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AddressId") - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("Identitifer") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.Property("Street") - .HasColumnType("longtext"); - - b.Property("UseDarkMode") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Customer"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.cs deleted file mode 100644 index b98af064..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.cs +++ /dev/null @@ -1,603 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.EntityEvents.Subscriber.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Customer", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Identitifer = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProfileId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AddressId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), - UseDarkMode = table.Column(type: "tinyint(1)", nullable: false), - Street = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Customer", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Customer_CreatedAt", - table: "Customer", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Customer_IsDeleted", - table: "Customer", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Customer"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index cd1e7608..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,662 +0,0 @@ -// -using System; -using Api.Data.EntityEvents.Subscriber.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.EntityEvents.Subscriber.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.EntityEvents.Subscriber.Models.Customer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AddressId") - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("Identitifer") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.Property("Street") - .HasColumnType("longtext"); - - b.Property("UseDarkMode") - .HasColumnType("tinyint(1)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Customer"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Program.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Program.cs deleted file mode 100644 index 7d99fdf0..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Program.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Api.Data.EntityEvents.Subscriber.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; -using Nano.Eventing.Extensions; -using Nano.Eventing.RabbitMq; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - x.AddNanoEventing(); - }) - .Build() - .Run(); diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Properties/InternalsVisibleTo.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Properties/InternalsVisibleTo.cs deleted file mode 100644 index a49dcfd1..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.EntityEvents")] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json deleted file mode 100644 index 4de4ef5b..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb2;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Production.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Staging.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.json deleted file mode 100644 index 3b1ce56d..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - }, - "Eventing": { - "Host": "rabbitmq", - "Credentials": { - "Id": "rabbitmq_user", - "Secret": "password" - } - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.sln b/Api.Data.EntityEvents/Api.Data.EntityEvents.sln deleted file mode 100644 index e024eddf..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents.sln +++ /dev/null @@ -1,177 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" - ProjectSection(SolutionItems) = preProject - .docker\init.sql = .docker\init.sql - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.EntityEvents.Models", "Api.Data.EntityEvents.Models\Api.Data.EntityEvents.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.EntityEvents", "Api.Data.EntityEvents\Api.Data.EntityEvents.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.EntityEvents", ".tests\Tests.Api.Data.EntityEvents\Tests.Api.Data.EntityEvents.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing", "..\..\Nano.Library\Nano.Eventing\Nano.Eventing.csproj", "{A8E623BC-70EA-3CC8-AFAD-797F006C4A41}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.RabbitMq", "..\..\Nano.Library\Nano.Eventing.RabbitMq\Nano.Eventing.RabbitMq.csproj", "{0789C863-B371-F968-B9C9-5F3CF3DD9897}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.EntityEvents.Subscriber.Models", "Api.Data.EntityEvents.Subscriber.Models\Api.Data.EntityEvents.Subscriber.Models.csproj", "{129D3824-38AE-3CF2-8038-7180E9F0DA51}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.EntityEvents.Subscriber", "Api.Data.EntityEvents.Subscriber\Api.Data.EntityEvents.Subscriber.csproj", "{EC5580EC-22B1-D1D7-4A59-787F34506C86}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.Build.0 = Release|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.Build.0 = Release|Any CPU - {129D3824-38AE-3CF2-8038-7180E9F0DA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {129D3824-38AE-3CF2-8038-7180E9F0DA51}.Debug|Any CPU.Build.0 = Debug|Any CPU - {129D3824-38AE-3CF2-8038-7180E9F0DA51}.Release|Any CPU.ActiveCfg = Release|Any CPU - {129D3824-38AE-3CF2-8038-7180E9F0DA51}.Release|Any CPU.Build.0 = Release|Any CPU - {EC5580EC-22B1-D1D7-4A59-787F34506C86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC5580EC-22B1-D1D7-4A59-787F34506C86}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC5580EC-22B1-D1D7-4A59-787F34506C86}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC5580EC-22B1-D1D7-4A59-787F34506C86}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {0789C863-B371-F968-B9C9-5F3CF3DD9897} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Api.Data.EntityEvents.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents/Api.Data.EntityEvents.csproj deleted file mode 100644 index 32b1a410..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Api.Data.EntityEvents.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/AddresssController.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/AddresssController.cs deleted file mode 100644 index 8863ec75..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/AddresssController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.EntityEvents.Models; -using Api.Data.EntityEvents.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.EntityEvents.Controllers; - -/// -/// Controller with addresses. -/// -/// The . -/// The . -public class AddresssController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/CustomersController.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/CustomersController.cs deleted file mode 100644 index 4abfbf15..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/CustomersController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.EntityEvents.Models; -using Api.Data.EntityEvents.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.EntityEvents.Controllers; - -/// -/// Controller with customers. -/// -/// The . -/// The . -public class CustomersController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/OrdersController.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/OrdersController.cs deleted file mode 100644 index 6c26eb0e..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/OrdersController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.EntityEvents.Models; -using Api.Data.EntityEvents.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.EntityEvents.Controllers; - -/// -/// Controller with Orders. -/// -/// The . -/// The . -public class OrdersController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/ProfilesController.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/ProfilesController.cs deleted file mode 100644 index 52dbcd93..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/ProfilesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.EntityEvents.Models; -using Api.Data.EntityEvents.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.EntityEvents.Controllers; - -/// -/// Controller with profiles. -/// -/// The . -/// The . -public class ProfilesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/AddressMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/AddressMapping.cs deleted file mode 100644 index 6c3de7be..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/AddressMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.EntityEvents.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.EntityEvents.Data.Mappings; - -/// -public class AddressMapping : BaseEntityMapping
-{ - /// - public override void Configure(EntityTypeBuilder
builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Street) - .IsRequired(); - - builder - .HasOne(x => x.Profile) - .WithOne(x => x.Address); - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/CustomerMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/CustomerMapping.cs deleted file mode 100644 index 43eb1a75..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/CustomerMapping.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using Api.Data.EntityEvents.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; -using Nano.Data.Mappings.Extensions; - -namespace Api.Data.EntityEvents.Data.Mappings; - -/// -public class CustomerMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name) - .IsRequired(); - - builder - .HasIndex(x => x.Name); - - builder - .HasOne(x => x.Profile) - .WithMany(x => x.Customers) - .IsRequired(); - - builder - .HasMany(x => x.Orders) - .WithOne(x => x.Customer) - .IsRequired(); - - builder - .OnInserting(x => - { - x.Entity.Name += "-triggred"; - }); - - builder - .OnUpdating(x => - { - x.Entity.Name += "-triggred"; - }); - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/OrderMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/OrderMapping.cs deleted file mode 100644 index 17610e87..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/OrderMapping.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Api.Data.EntityEvents.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.EntityEvents.Data.Mappings; - -/// -public class OrderMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .HasOne(x => x.Customer) - .WithMany(x => x.Orders) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/PersonMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/PersonMapping.cs deleted file mode 100644 index 441a62ab..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/PersonMapping.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Api.Data.EntityEvents.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.EntityEvents.Data.Mappings; - -/// -public class PersonMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Identitifer) - .IsRequired() - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/ProfileMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/ProfileMapping.cs deleted file mode 100644 index afd5b7d5..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/ProfileMapping.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Api.Data.EntityEvents.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.EntityEvents.Data.Mappings; - -/// -public class ProfileMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .HasOne(x => x.Address) - .WithOne(x => x.Profile); - - builder - .HasMany(x => x.Customers) - .WithOne(x => x.Profile) - .IsRequired(); - - builder - .OwnsOne(x => x.Settings) - .Property(x => x.UseDarkMode) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContext.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContext.cs deleted file mode 100644 index 3b4e54df..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.EntityEvents.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContextFactory.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 20fc0ad9..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.EntityEvents.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local b/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local deleted file mode 100644 index b78f25ae..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.EntityEvents.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.Designer.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.Designer.cs deleted file mode 100644 index edd93908..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.Designer.cs +++ /dev/null @@ -1,849 +0,0 @@ -// -using System; -using Api.Data.EntityEvents.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.EntityEvents.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260412140549_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Address", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Street") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Address"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("Identitifer") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.HasIndex("ProfileId"); - - b.ToTable("Customer"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("CustomerId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("CustomerId"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Order"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("Identitifer") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Person"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AddressId") - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("AddressId") - .IsUnique() - .HasDatabaseName("UX_Profile_AddressId"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Profile"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => - { - b.HasOne("Api.Data.EntityEvents.Models.Profile", "Profile") - .WithMany("Customers") - .HasForeignKey("ProfileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Order", b => - { - b.HasOne("Api.Data.EntityEvents.Models.Customer", "Customer") - .WithMany("Orders") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Customer"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => - { - b.HasOne("Api.Data.EntityEvents.Models.Address", "Address") - .WithOne("Profile") - .HasForeignKey("Api.Data.EntityEvents.Models.Profile", "AddressId"); - - b.OwnsOne("Api.Data.EntityEvents.Models.Owned.ProfileSettings", "Settings", b1 => - { - b1.Property("ProfileId") - .HasColumnType("char(36)"); - - b1.Property("UseDarkMode") - .HasColumnType("tinyint(1)"); - - b1.HasKey("ProfileId"); - - b1.ToTable("Profile"); - - b1.WithOwner() - .HasForeignKey("ProfileId"); - }); - - b.Navigation("Address"); - - b.Navigation("Settings") - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Address", b => - { - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => - { - b.Navigation("Orders"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => - { - b.Navigation("Customers"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.cs deleted file mode 100644 index 539a4bb9..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.cs +++ /dev/null @@ -1,756 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.EntityEvents.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Address", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Street = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Address", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Person", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Identitifer = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Person", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Profile", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AddressId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), - Settings_UseDarkMode = table.Column(type: "tinyint(1)", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Profile", x => x.Id); - table.ForeignKey( - name: "FK_Profile_Address_AddressId", - column: x => x.AddressId, - principalTable: "Address", - principalColumn: "Id"); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Customer", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProfileId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - Identitifer = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK_Customer", x => x.Id); - table.ForeignKey( - name: "FK_Customer_Profile_ProfileId", - column: x => x.ProfileId, - principalTable: "Profile", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Order", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CustomerId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Order", x => x.Id); - table.ForeignKey( - name: "FK_Order_Customer_CustomerId", - column: x => x.CustomerId, - principalTable: "Customer", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Address_CreatedAt", - table: "Address", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Address_IsDeleted", - table: "Address", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Customer_CreatedAt", - table: "Customer", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Customer_IsDeleted", - table: "Customer", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Customer_Name", - table: "Customer", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_Customer_ProfileId", - table: "Customer", - column: "ProfileId"); - - migrationBuilder.CreateIndex( - name: "IX_Order_CreatedAt", - table: "Order", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Order_CustomerId", - table: "Order", - column: "CustomerId"); - - migrationBuilder.CreateIndex( - name: "IX_Order_IsDeleted", - table: "Order", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Person_CreatedAt", - table: "Person", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Person_IsDeleted", - table: "Person", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Profile_CreatedAt", - table: "Profile", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Profile_IsDeleted", - table: "Profile", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "UX_Profile_AddressId", - table: "Profile", - column: "AddressId", - unique: true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Order"); - - migrationBuilder.DropTable( - name: "Person"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "Customer"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - - migrationBuilder.DropTable( - name: "Profile"); - - migrationBuilder.DropTable( - name: "Address"); - } - } -} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index ce727baf..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,846 +0,0 @@ -// -using System; -using Api.Data.EntityEvents.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.EntityEvents.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Address", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Street") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Address"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("Identitifer") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.HasIndex("ProfileId"); - - b.ToTable("Customer"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("CustomerId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("CustomerId"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Order"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Person", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("Identitifer") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Person"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AddressId") - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("AddressId") - .IsUnique() - .HasDatabaseName("UX_Profile_AddressId"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Profile"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => - { - b.HasOne("Api.Data.EntityEvents.Models.Profile", "Profile") - .WithMany("Customers") - .HasForeignKey("ProfileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Order", b => - { - b.HasOne("Api.Data.EntityEvents.Models.Customer", "Customer") - .WithMany("Orders") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Customer"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => - { - b.HasOne("Api.Data.EntityEvents.Models.Address", "Address") - .WithOne("Profile") - .HasForeignKey("Api.Data.EntityEvents.Models.Profile", "AddressId"); - - b.OwnsOne("Api.Data.EntityEvents.Models.Owned.ProfileSettings", "Settings", b1 => - { - b1.Property("ProfileId") - .HasColumnType("char(36)"); - - b1.Property("UseDarkMode") - .HasColumnType("tinyint(1)"); - - b1.HasKey("ProfileId"); - - b1.ToTable("Profile"); - - b1.WithOwner() - .HasForeignKey("ProfileId"); - }); - - b.Navigation("Address"); - - b.Navigation("Settings") - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Address", b => - { - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => - { - b.Navigation("Orders"); - }); - - modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => - { - b.Navigation("Customers"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Program.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Program.cs deleted file mode 100644 index 040619d0..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Program.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Api.Data.EntityEvents.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; -using Nano.Eventing.Extensions; -using Nano.Eventing.RabbitMq; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - x.AddNanoEventing(); - }) - .Build() - .Run(); diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Properties/InternalsVisibleTo.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Properties/InternalsVisibleTo.cs deleted file mode 100644 index a49dcfd1..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.EntityEvents")] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Production.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Staging.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.json deleted file mode 100644 index 3b1ce56d..00000000 --- a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - }, - "Eventing": { - "Host": "rabbitmq", - "Credentials": { - "Id": "rabbitmq_user", - "Secret": "password" - } - } -} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Dockerfile b/Api.Data.EntityEvents/Dockerfile deleted file mode 100644 index dca7eca5..00000000 --- a/Api.Data.EntityEvents/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.EntityEvents.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/LICENSE b/Api.Data.EntityEvents/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.EntityEvents/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.EntityEvents/README.md b/Api.Data.EntityEvents/README.md deleted file mode 100644 index d4991a41..00000000 --- a/Api.Data.EntityEvents/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Api.Data.EntityEvents - -> _Nano API application with data entity events._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to -demonstrate entity events. Additionally, eventing has been enabled mirroring the setup from **[Api.Eventing.RabbityMq](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Eventing.RabbityMq)**, -but any eventing provider can be used. New entity controllers have been added for use with showcasing entity events. - -An additional application has been added to the solution. In a typical architecture, this application would be placed in a separate solution. However, for the purpose of demonstrating -entity events in Nano, it has been included within the same solution. This setup allows the second application to receive and handle the published entity events from the first application, -enabling a complete end-to-end event flow for demonstration purposes. - -A set of entities has been introduced to demonstrate the eventing behavior. The core structure consists of a `Customer` entity that derives from `Person`. A `Customer` contains -a `Profile`, which in turn contains an `Address`. In addition, the `Customer` has a collection of `Order` entities, and the `Profile` includes an owned navigation property. The full -entity relationships can be inspected directly in the codebase. - -Also an `OnInserting` and `OnUpdating` trigger has been mapped for `Customer`, to show how the entity events will get the updated value. - -> 📖 Learn more about **[Nano Data Triggers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#triggers)**. - -Both `Person` and `Customer` are annotated with the `PublishAttribute`, and define property names that determine which fields should trigger publish events on added, modified, -and deleted actions. Several controllers have also been added to support these entities, enabling various create, update, and delete operations to trigger `Customer` entity events. When -a `Customer` is added, modified, or deleted directly, an event is published. However, changes to dependent navigation properties will also result in a `Modified` event being published -for the `Customer`. - -It is also important to note that `Customer` inherits publishable property definitions from `Person`. To ensure all property names defined across the entire inheritance hierarchy are -included, Nano aggregates the `PublishAttribute` metadata and hydrates entities both forward (for direct changes) and in reverse (via foreign key relationships) to capture deferred changes. - -> 📖 Learn more about **[Nano Data Entity Events](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#entity-events)**. diff --git a/Api.Data.EntityEvents/icon.png b/Api.Data.EntityEvents/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml b/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml deleted file mode 100644 index d7305c76..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.identity.auth.apikey: - image: api.data.identity.auth.apikey - hostname: api-data-auth-apikey - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.Identity.Auth.ApiKey - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.Identity.Auth.ApiKey/.dockerignore b/Api.Data.Identity.Auth.ApiKey/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.Identity.Auth.ApiKey/.github/config/slack.yml b/Api.Data.Identity.Auth.ApiKey/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 059497a6..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,226 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.Identity.Auth.ApiKey - IMAGE_NAME: api.data.identity.auth.apikey - SERVICE_NAME: api-data-identity.auth.apikey - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - AUTH_API_KEY_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_API_KEY_SECRET || secrets.STAGING_AUTH_API_KEY_SECRET }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo kubectl create secret generic auth-api-key-secret --from-literal=apikey-secret=$env:AUTH_API_KEY_SECRET --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/.gitignore b/Api.Data.Identity.Auth.ApiKey/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/autoscaler.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml deleted file mode 100644 index e5d985b7..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml +++ /dev/null @@ -1,104 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - - name: Data__Identity__ApiKey - valueFrom: - secretKeyRef: - name: auth-api-key-secret - key: apikey-secret - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/service.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Properties/DoNotParallelize.cs b/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Tests.Api.Data.Identity.Auth.ApiKey.csproj b/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Tests.Api.Data.Identity.Auth.ApiKey.csproj deleted file mode 100644 index ceb6be59..00000000 --- a/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Tests.Api.Data.Identity.Auth.ApiKey.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Criterias/UserQueryCriteria.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Criterias/UserQueryCriteria.cs deleted file mode 100644 index e2a95cb1..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Criterias/UserQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.Identity.Auth.ApiKey.Models.Criterias; - -/// -public class UserQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(User.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/User.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/User.cs deleted file mode 100644 index df611417..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/User.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Identity.Auth.ApiKey.Models; - -/// -/// User. -/// -public class User : BaseEntityUser -{ - /// - /// Name. - /// - [Required] - [MaxLength(128)] - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.sln b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.sln deleted file mode 100644 index 7acbb800..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.ApiKey.Models", "Api.Data.Identity.Auth.ApiKey.Models\Api.Data.Identity.Auth.ApiKey.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.ApiKey", "Api.Data.Identity.Auth.ApiKey\Api.Data.Identity.Auth.ApiKey.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Identity.Auth.ApiKey", ".tests\Tests.Api.Data.Identity.Auth.ApiKey\Tests.Api.Data.Identity.Auth.ApiKey.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.csproj b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.csproj deleted file mode 100644 index 9255b1f9..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/AuthController.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/AuthController.cs deleted file mode 100644 index 4d792d7a..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/AuthController.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Api.Mvc.Authentication.Abstractions; - -namespace Api.Data.Identity.Auth.ApiKey.Controllers; - -/// -public class AuthController(ILogger logger, IAuthRepository authRepository) - : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/UsersController.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/UsersController.cs deleted file mode 100644 index aced14ef..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/UsersController.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Api.Data.Identity.Auth.ApiKey.Models; -using Api.Data.Identity.Auth.ApiKey.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; -using Nano.Data.Abstractions.Identity; - -namespace Api.Data.Identity.Auth.ApiKey.Controllers; - -/// -public class UsersController(ILogger logger, IRepository repository, IIdentityRepository identityRepository) - : BaseEntityUserController(logger, repository, identityRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/Mappings/UserMapping.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/Mappings/UserMapping.cs deleted file mode 100644 index 5da560ef..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/Mappings/UserMapping.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Api.Data.Identity.Auth.ApiKey.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings.Identity; - -namespace Api.Data.Identity.Auth.ApiKey.Data.Mappings; - -/// -public class UserMapping : BaseEntityUserMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - if (builder == null) - throw new ArgumentNullException(nameof(builder)); - - base.Configure(builder); - - builder - .Property(x => x.Name) - .HasMaxLength(128) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContext.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContext.cs deleted file mode 100644 index 87e6c99b..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.Identity.Auth.ApiKey.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContextFactory.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 435fc1c7..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.Identity.Auth.ApiKey.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local deleted file mode 100644 index bab003df..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.ApiKey.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.Designer.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.Designer.cs deleted file mode 100644 index f68b351d..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.Designer.cs +++ /dev/null @@ -1,660 +0,0 @@ -// -using System; -using Api.Data.Identity.Auth.ApiKey.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Identity.Auth.ApiKey.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415143026_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Identity.Auth.ApiKey.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Identity.Auth.ApiKey.Models.User", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Api.Data.Identity.Auth.ApiKey.Models.User", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.cs deleted file mode 100644 index 87be3984..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.Identity.Auth.ApiKey.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "User", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_User", x => x.Id); - table.ForeignKey( - name: "FK_User___EFIdentityUser_Id", - column: x => x.Id, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_User_CreatedAt", - table: "User", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_User_IsDeleted", - table: "User", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "User"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 0303f8b2..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,657 +0,0 @@ -// -using System; -using Api.Data.Identity.Auth.ApiKey.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Identity.Auth.ApiKey.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Identity.Auth.ApiKey.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Identity.Auth.ApiKey.Models.User", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Api.Data.Identity.Auth.ApiKey.Models.User", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Program.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Program.cs deleted file mode 100644 index 6899a140..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.Identity.Auth.ApiKey.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Properties/InternalsVisibleTo.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Properties/InternalsVisibleTo.cs deleted file mode 100644 index eb7f4939..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.Identity.Auth.ApiKey")] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json deleted file mode 100644 index 223c8a7b..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.development", - "Audience": "nano.development", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", - "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV" - } - }, - "Documentation": { - } - }, - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Production.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Production.json deleted file mode 100644 index 8ec28d0d..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Production.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.production", - "Audience": "nano.production" - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Staging.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Staging.json deleted file mode 100644 index 0d8d49d3..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Staging.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.staging", - "Audience": "nano.staging" - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.json deleted file mode 100644 index 719ba02e..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - }, - "Documentation": { - } - }, - "Data": { - "ConnectionString": null, - "Identity": { - "TokensExpiration": "24:00:00", - "UseAudit": "All", - "User": { - "IsUniqueEmailAddressRequired": true, - "IsUniquePhoneNumberRequired": false, - "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", - "DefaultRoles": [ - "administrator" - ] - }, - "SignIn": { - "RequireConfirmedEmail": false, - "RequireConfirmedPhoneNumber": false - }, - "Lockout": { - "AllowedForNewUsers": true, - "MaxFailedAccessAttempts": 3, - "DefaultLockoutTimeSpan": "00:30:00" - }, - "Password": { - "RequireDigit": true, - "RequireNonAlphanumeric": true, - "RequireLowercase": true, - "RequirUppercase": true, - "RequiredLength": 12, - "RequiredUniqueCharacters": 3 - }, - "ApiKey": { - "Secret": "secret" - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Dockerfile b/Api.Data.Identity.Auth.ApiKey/Dockerfile deleted file mode 100644 index 30ab00dc..00000000 --- a/Api.Data.Identity.Auth.ApiKey/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.ApiKey.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/LICENSE b/Api.Data.Identity.Auth.ApiKey/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.Identity.Auth.ApiKey/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/README.md b/Api.Data.Identity.Auth.ApiKey/README.md deleted file mode 100644 index c3be91d9..00000000 --- a/Api.Data.Identity.Auth.ApiKey/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# Api.Data.Identity.Auth.ApiKey - -> _Nano API application with data identity api-key authentication._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Data.Identity.Auth.Jwt](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.Identity.Auth.Jwt)**. - -The service has been configured for API key authentication. Nothing else has changed. It demonstrates how to manage API keys in Nano, and how to use both JWT and API key -authentication concurrently. Use the `auth/login/apikey` to authenticate and receive a JWT token, to use in subsequent requests. - -Try out the different API key identity and authentication methods. - -API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration -are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -The following endpoint from the auth controller is available for testing: - -| Endpoint | Description | -| -------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/auth/login/apikey` | Authenticates the user using `X-Api-Key` header value and returns an access token (JWT). | -| `http://localhost:8080/api/auth/login` | Authenticates a user and returns an access token (JWT). | -| `http://localhost:8080/api/auth/login/refresh` | Refreshes an existing access token. | -| `http://localhost:8080/api/auth/logout` | Logs out the current user. | - -Additionally, the identity controller is also avaialble, and the actions can be used for testing authorization. - -> 📖 Learn more about **[Nano API Key Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#authentication)**. - -## Configuration -Configured the application with the necessary authentication setup, in addition to the existing identity and jwt configuration. - -```json -"App": { - "Data": { - "Identity": { - "ApiKey": { - "Secret": "secret" - } - } - } -} -``` - -## Kubernetes -For `Staging` and `Production` environments, a secret must be created to securely store the apikey secret. Below demonstrates how to map the secret containing the API key secret. - -```yaml -spec: - template: - spec: - containers: - env: - - name: Data__Identity__ApiKey - valueFrom: - secretKeyRef: - name: auth-api-key-secret - key: apikey-secret -``` - -## GitHub Action -The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. - -```yaml -env: - AUTH_API_KEY_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_API_KEY_SECRET || secrets.STAGING_AUTH_API_KEY_SECRET }} -``` - -...and created during the Kubernetes deploy step. - -```yaml -sudo kubectl create secret generic auth-api-key-secret --from-literal=apikey-secret=$env:AUTH_API_KEY_SECRET --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Api.Data.Identity.Auth.ApiKey/icon.png b/Api.Data.Identity.Auth.ApiKey/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml b/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml deleted file mode 100644 index 921f1be5..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.identity.auth.external.custom: - image: api.data.identity.auth.external.custom - hostname: api-data-auth-external-custom - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.Identity.Auth.External.Custom - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.Identity.Auth.External.Custom/.dockerignore b/Api.Data.Identity.Auth.External.Custom/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.Identity.Auth.External.Custom/.github/config/slack.yml b/Api.Data.Identity.Auth.External.Custom/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 1f17e94f..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,219 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.Identity.Auth.External.Custom - IMAGE_NAME: api.data.identity.auth.external.custom - SERVICE_NAME: api-data-identity.auth.external.custom - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/.gitignore b/Api.Data.Identity.Auth.External.Custom/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/autoscaler.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml deleted file mode 100644 index d7fb451d..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml +++ /dev/null @@ -1,99 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/service.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Properties/DoNotParallelize.cs b/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Tests.Api.Data.Identity.Auth.External.Custom.csproj b/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Tests.Api.Data.Identity.Auth.External.Custom.csproj deleted file mode 100644 index f4bf561a..00000000 --- a/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Tests.Api.Data.Identity.Auth.External.Custom.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Criterias/UserQueryCriteria.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Criterias/UserQueryCriteria.cs deleted file mode 100644 index 0fa1f6cf..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Criterias/UserQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.Identity.Auth.External.Custom.Models.Criterias; - -/// -public class UserQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(User.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/User.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/User.cs deleted file mode 100644 index 29ac4e15..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/User.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Identity.Auth.External.Custom.Models; - -/// -/// User. -/// -public class User : BaseEntityUser -{ - /// - /// Name. - /// - [Required] - [MaxLength(128)] - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.sln b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.sln deleted file mode 100644 index b859e80a..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.External.Custom.Models", "Api.Data.Identity.Auth.External.Custom.Models\Api.Data.Identity.Auth.External.Custom.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.External.Custom", "Api.Data.Identity.Auth.External.Custom\Api.Data.Identity.Auth.External.Custom.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Identity.Auth.External.Custom", ".tests\Tests.Api.Data.Identity.Auth.External.Custom\Tests.Api.Data.Identity.Auth.External.Custom.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.csproj b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.csproj deleted file mode 100644 index b3372b08..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs deleted file mode 100644 index 7dd866ed..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Nano.App.Api.Mvc.Authentication; -using Nano.Data.Abstractions.Identity.Authentication.Models; - -namespace Api.Data.Identity.Auth.External.Custom.Authentication; - -/// -public class ExternalProviderCustomRepository() : BaseAuthExternalRepository("Custom") -{ - /// - public override async Task AuthenticateAsync(ImplicitFlow flow, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return new ExternalAuthenticationData - { - Id = "external-id", - Username = "MyUser", - EmailAddress = "johndoe@domain.com", - PhoneNumber = "+4520111112", - Name = "John Doe", - ExternalToken = new ExternalAuthenticationToken - { - Name = this.ProviderName, - Token = "token", - RefreshToken = "refresh-token" - } - }; - } - - /// - public override async Task AuthenticateRefreshAsync(string refreshToken, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return new ExternalAuthenticationToken - { - Name = this.ProviderName, - Token = "token", - RefreshToken = "refresh-token" - }; - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/AuthController.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/AuthController.cs deleted file mode 100644 index 3e7aaac9..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/AuthController.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Api.Mvc.Authentication.Abstractions; - -namespace Api.Data.Identity.Auth.External.Custom.Controllers; - -/// -public class AuthController(ILogger logger, IAuthRepository authRepository) - : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/UsersController.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/UsersController.cs deleted file mode 100644 index ff8a1dee..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/UsersController.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Api.Data.Identity.Auth.External.Custom.Models; -using Api.Data.Identity.Auth.External.Custom.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; -using Nano.Data.Abstractions.Identity; - -namespace Api.Data.Identity.Auth.External.Custom.Controllers; - -/// -public class UsersController(ILogger logger, IRepository repository, IIdentityRepository identityRepository) - : BaseEntityUserController(logger, repository, identityRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/Mappings/UserMapping.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/Mappings/UserMapping.cs deleted file mode 100644 index be5206c1..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/Mappings/UserMapping.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Api.Data.Identity.Auth.External.Custom.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings.Identity; - -namespace Api.Data.Identity.Auth.External.Custom.Data.Mappings; - -/// -public class UserMapping : BaseEntityUserMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - if (builder == null) - throw new ArgumentNullException(nameof(builder)); - - base.Configure(builder); - - builder - .Property(x => x.Name) - .HasMaxLength(128) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContext.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContext.cs deleted file mode 100644 index b8d78965..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.Identity.Auth.External.Custom.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContextFactory.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 53d00253..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.Identity.Auth.External.Custom.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local deleted file mode 100644 index a00c6450..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.Designer.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.Designer.cs deleted file mode 100644 index 008df313..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.Designer.cs +++ /dev/null @@ -1,660 +0,0 @@ -// -using System; -using Api.Data.Identity.Auth.External.Custom.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Identity.Auth.External.Custom.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415143439_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Identity.Auth.External.Custom.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Identity.Auth.External.Custom.Models.User", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Api.Data.Identity.Auth.External.Custom.Models.User", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.cs deleted file mode 100644 index 1fef0424..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.Identity.Auth.External.Custom.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "User", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_User", x => x.Id); - table.ForeignKey( - name: "FK_User___EFIdentityUser_Id", - column: x => x.Id, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_User_CreatedAt", - table: "User", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_User_IsDeleted", - table: "User", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "User"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 9bd7b87b..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,657 +0,0 @@ -// -using System; -using Api.Data.Identity.Auth.External.Custom.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Identity.Auth.External.Custom.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Identity.Auth.External.Custom.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Identity.Auth.External.Custom.Models.User", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Api.Data.Identity.Auth.External.Custom.Models.User", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Program.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Program.cs deleted file mode 100644 index 3a3caa58..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.Identity.Auth.External.Custom.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Properties/InternalsVisibleTo.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 0865fd1f..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.Identity.Auth.External.Custom")] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json deleted file mode 100644 index 223c8a7b..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.development", - "Audience": "nano.development", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", - "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV" - } - }, - "Documentation": { - } - }, - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Production.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Production.json deleted file mode 100644 index 8ec28d0d..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Production.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.production", - "Audience": "nano.production" - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Staging.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Staging.json deleted file mode 100644 index 0d8d49d3..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Staging.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.staging", - "Audience": "nano.staging" - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.json deleted file mode 100644 index 2494e2d7..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - }, - "Documentation": { - } - }, - "Data": { - "ConnectionString": null, - "Identity": { - "TokensExpiration": "24:00:00", - "UseAudit": "All", - "User": { - "IsUniqueEmailAddressRequired": true, - "IsUniquePhoneNumberRequired": false, - "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", - "DefaultRoles": [ - "administrator" - ] - }, - "SignIn": { - "RequireConfirmedEmail": false, - "RequireConfirmedPhoneNumber": false - }, - "Lockout": { - "AllowedForNewUsers": true, - "MaxFailedAccessAttempts": 3, - "DefaultLockoutTimeSpan": "00:30:00" - }, - "Password": { - "RequireDigit": true, - "RequireNonAlphanumeric": true, - "RequireLowercase": true, - "RequirUppercase": true, - "RequiredLength": 12, - "RequiredUniqueCharacters": 3 - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Dockerfile b/Api.Data.Identity.Auth.External.Custom/Dockerfile deleted file mode 100644 index 5a2e6a5e..00000000 --- a/Api.Data.Identity.Auth.External.Custom/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/LICENSE b/Api.Data.Identity.Auth.External.Custom/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.Identity.Auth.External.Custom/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/README.md b/Api.Data.Identity.Auth.External.Custom/README.md deleted file mode 100644 index 0f9af004..00000000 --- a/Api.Data.Identity.Auth.External.Custom/README.md +++ /dev/null @@ -1,86 +0,0 @@ -# Api.Data.Identity.Auth.External.Custom - -> _Nano API application with data identity external custom authentication._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Data.Identity.Auth.Jwt](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.Identity.Auth.Jwt)**. - -The JWT authentication scheme remains configured, and a `BaseAuthExternalRepository` implementation named `ExternalProviderCustomRepository`, with `Custom` as provider-name, -has been added. As a result, additional endpoints from the `AuthController` are now exposed, as shown below. - -The `ExternalProviderCustomRepository` always succeeds and returns static (mocked) data. It serves as a simple example of how to implement a custom external authentication -provider in Nano. - -API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration -are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -The following endpoint from the auth controller is available for testing: - -| Endpoint | Description | -| ------------------------------------------------------- | ---------------------------------------------------------------------------------- | -| `http://localhost:8080/api/auth/external/schemes` | Retrieves all configured external authentication methods, e.g., Google, Facebook. | -| `http://localhost:8080/api/auth/login` | Authenticates a user and returns an access token (JWT). | -| `http://localhost:8080/api/auth/login/external/custom` | Signs in a user using external custom authentication | -| `http://localhost:8080/api/auth/login/refresh` | Refreshes an existing access token. | -| `http://localhost:8080/api/auth/logout` | Logs out the current user. | - -The following new endpoints related to the custom authentication provider from the identity controller is shown below. - -| Endpoint | Description | -| --------------------------------------------------------------------- | ---------------------------------------------------------- | -| `http://localhost:8080/api/users/signup/external/custom` | Signs up a user using external custom authentication | -| `http://localhost:8080/api/users/{id}/external-logins` | Retrieves all external authentication methods of a user. | -| `http://localhost:8080/api/users/{id}/external-logins/add/custom` | Adds a `Custom` external login to a user account. | -| `http://localhost:8080/api/users/{id}/external-logins/remove/custom` | Removes an `Custom` login from a user account. | - -> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#authentication)**. - -## Configuration -Configured the application with the necessary authentication setup, in addition to the identity configuration. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - } -} -``` - -...and for `appesettings.Developmnet.json`. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", - "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", - "Expiration": "24:00:00" - } - } -} -``` diff --git a/Api.Data.Identity.Auth.External.Custom/icon.png b/Api.Data.Identity.Auth.External.Custom/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml b/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml deleted file mode 100644 index 69136e45..00000000 --- a/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.identity.auth.jwt: - image: api.data.identity.auth.jwt - hostname: api-data-auth-jwt - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.Identity.Auth.Jwt - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.Identity.Auth.Jwt/.dockerignore b/Api.Data.Identity.Auth.Jwt/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.Identity.Auth.Jwt/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.Identity.Auth.Jwt/.github/config/slack.yml b/Api.Data.Identity.Auth.Jwt/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.Identity.Auth.Jwt/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 43761bfc..00000000 --- a/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,219 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.Identity.Auth.Jwt - IMAGE_NAME: api.data.identity.auth.jwt - SERVICE_NAME: api-data-identity.auth.jwt - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/.gitignore b/Api.Data.Identity.Auth.Jwt/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.Identity.Auth.Jwt/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/autoscaler.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.Identity.Auth.Jwt/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml deleted file mode 100644 index d7fb451d..00000000 --- a/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml +++ /dev/null @@ -1,99 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/service.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.Identity.Auth.Jwt/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Properties/DoNotParallelize.cs b/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Tests.Api.Data.Identity.Auth.Jwt.csproj b/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Tests.Api.Data.Identity.Auth.Jwt.csproj deleted file mode 100644 index 50c76a68..00000000 --- a/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Tests.Api.Data.Identity.Auth.Jwt.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Criterias/UserQueryCriteria.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Criterias/UserQueryCriteria.cs deleted file mode 100644 index 671a3309..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Criterias/UserQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.Identity.Auth.Jwt.Models.Criterias; - -/// -public class UserQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(User.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/User.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/User.cs deleted file mode 100644 index 11e267fb..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/User.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Identity.Auth.Jwt.Models; - -/// -/// User. -/// -public class User : BaseEntityUser -{ - /// - /// Name. - /// - [Required] - [MaxLength(128)] - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.sln b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.sln deleted file mode 100644 index 18297930..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.Jwt.Models", "Api.Data.Identity.Auth.Jwt.Models\Api.Data.Identity.Auth.Jwt.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.Jwt", "Api.Data.Identity.Auth.Jwt\Api.Data.Identity.Auth.Jwt.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Identity.Auth.Jwt", ".tests\Tests.Api.Data.Identity.Auth.Jwt\Tests.Api.Data.Identity.Auth.Jwt.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.csproj b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.csproj deleted file mode 100644 index 165b0594..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/AuthController.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/AuthController.cs deleted file mode 100644 index 78c56093..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/AuthController.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Api.Mvc.Authentication.Abstractions; - -namespace Api.Data.Identity.Auth.Jwt.Controllers; - -/// -public class AuthController(ILogger logger, IAuthRepository authRepository) - : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/UsersController.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/UsersController.cs deleted file mode 100644 index b0695aa1..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/UsersController.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Api.Data.Identity.Auth.Jwt.Models; -using Api.Data.Identity.Auth.Jwt.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; -using Nano.Data.Abstractions.Identity; - -namespace Api.Data.Identity.Auth.Jwt.Controllers; - -/// -public class UsersController(ILogger logger, IRepository repository, IIdentityRepository identityRepository) - : BaseEntityUserController(logger, repository, identityRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/Mappings/UserMapping.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/Mappings/UserMapping.cs deleted file mode 100644 index 7ee8ae00..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/Mappings/UserMapping.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Api.Data.Identity.Auth.Jwt.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings.Identity; - -namespace Api.Data.Identity.Auth.Jwt.Data.Mappings; - -/// -public class UserMapping : BaseEntityUserMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - if (builder == null) - throw new ArgumentNullException(nameof(builder)); - - base.Configure(builder); - - builder - .Property(x => x.Name) - .HasMaxLength(128) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContext.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContext.cs deleted file mode 100644 index acc59d18..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.Identity.Auth.Jwt.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContextFactory.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 2c77987f..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.Identity.Auth.Jwt.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local deleted file mode 100644 index 678ca81b..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.Jwt.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.Designer.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.Designer.cs deleted file mode 100644 index da7e84cb..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.Designer.cs +++ /dev/null @@ -1,660 +0,0 @@ -// -using System; -using Api.Data.Identity.Auth.Jwt.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Identity.Auth.Jwt.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415143540_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Identity.Auth.Jwt.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Identity.Auth.Jwt.Models.User", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Api.Data.Identity.Auth.Jwt.Models.User", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.cs deleted file mode 100644 index 724944a8..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.Identity.Auth.Jwt.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "User", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_User", x => x.Id); - table.ForeignKey( - name: "FK_User___EFIdentityUser_Id", - column: x => x.Id, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_User_CreatedAt", - table: "User", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_User_IsDeleted", - table: "User", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "User"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 617580b9..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,657 +0,0 @@ -// -using System; -using Api.Data.Identity.Auth.Jwt.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Identity.Auth.Jwt.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Identity.Auth.Jwt.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Identity.Auth.Jwt.Models.User", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Api.Data.Identity.Auth.Jwt.Models.User", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Program.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Program.cs deleted file mode 100644 index 2d2c524b..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.Identity.Auth.Jwt.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Properties/InternalsVisibleTo.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 2cb6248c..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.Identity.Auth.Jwt")] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json deleted file mode 100644 index 223c8a7b..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.development", - "Audience": "nano.development", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", - "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV" - } - }, - "Documentation": { - } - }, - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Production.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Production.json deleted file mode 100644 index 8ec28d0d..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Production.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.production", - "Audience": "nano.production" - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Staging.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Staging.json deleted file mode 100644 index 0d8d49d3..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Staging.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "App": { - "Authentication": { - "Jwt": { - "Issuer": "nano.staging", - "Audience": "nano.staging" - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.json deleted file mode 100644 index 2494e2d7..00000000 --- a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - }, - "Documentation": { - } - }, - "Data": { - "ConnectionString": null, - "Identity": { - "TokensExpiration": "24:00:00", - "UseAudit": "All", - "User": { - "IsUniqueEmailAddressRequired": true, - "IsUniquePhoneNumberRequired": false, - "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", - "DefaultRoles": [ - "administrator" - ] - }, - "SignIn": { - "RequireConfirmedEmail": false, - "RequireConfirmedPhoneNumber": false - }, - "Lockout": { - "AllowedForNewUsers": true, - "MaxFailedAccessAttempts": 3, - "DefaultLockoutTimeSpan": "00:30:00" - }, - "Password": { - "RequireDigit": true, - "RequireNonAlphanumeric": true, - "RequireLowercase": true, - "RequirUppercase": true, - "RequiredLength": 12, - "RequiredUniqueCharacters": 3 - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Dockerfile b/Api.Data.Identity.Auth.Jwt/Dockerfile deleted file mode 100644 index d166ebde..00000000 --- a/Api.Data.Identity.Auth.Jwt/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.Jwt.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/LICENSE b/Api.Data.Identity.Auth.Jwt/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.Identity.Auth.Jwt/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/README.md b/Api.Data.Identity.Auth.Jwt/README.md deleted file mode 100644 index 9824568f..00000000 --- a/Api.Data.Identity.Auth.Jwt/README.md +++ /dev/null @@ -1,118 +0,0 @@ -# Api.Data.Identity.Auth.Jwt - -> _Nano API application with data identity jwt authentication._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Data.Identity](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.Identity)** and adds a derived `AuthController`. - -Nothing else has changed for this example. The derived `AuthController` enables the identity users in the application to use the three endpoints inherited from -the `BaseAuthController`. - -API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration -are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -The following endpoint from the auth controller is available for testing: - -| Endpoint | Description | -| -------------------------------------------------- | ---------------------------------------------------------------------- | -| `http://localhost:8080/api/auth/login` | Authenticates a user and returns an access token (JWT). | -| `http://localhost:8080/api/auth/login/refresh` | Refreshes an existing access token. | -| `http://localhost:8080/api/auth/logout` | Logs out the current user. | - -Additionally, the identity controller is also avaialble, and the actions can be used for testing authorization. - -> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#authentication)**. - -## Configuration -Configured the application with the necessary authentication setup, in addition to the identity configuration. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": null, - "Audience": null, - "PublicKey": null, - "PrivateKey": null, - "Expiration": "01:00:00", - "RefreshExpiration": "72:00:00" - } - } -} -``` - -...and for `appesettings.Developmnet.json`. - -```json -"App": { - "Authentication": { - "Jwt": { - "Issuer": "Development.nano", - "Audience": "Development.nano", - "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", - "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", - "Expiration": "24:00:00" - } - } -} -``` - -## Kubernetes -For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be -enabled. Below demonstrates how to map the secret containing the JWT keys. - -```yaml -spec: - template: - spec: - containers: - env: - - name: App__Authentication__Jwt__PublicKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-public-key - - name: App__Authentication__Jwt__PrivateKey - valueFrom: - secretKeyRef: - name: auth-jwt-secret - key: jwt-private-key -``` - -## GitHub Action -The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. - -```yaml -env: - AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} - AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} -``` - -...and created during the Kubernetes deploy step. - -```yaml -sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` - diff --git a/Api.Data.Identity.Auth.Jwt/icon.png b/Api.Data.Identity.Auth.Jwt/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.Identity/.docker/docker-compose.yml b/Api.Data.Identity/.docker/docker-compose.yml deleted file mode 100644 index d4a7e47a..00000000 --- a/Api.Data.Identity/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.identity: - image: api.data.identity - hostname: api-data-identity - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.Identity - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.Identity/.dockerignore b/Api.Data.Identity/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.Identity/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.Identity/.github/config/slack.yml b/Api.Data.Identity/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.Identity/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Identity/.github/workflows/build-and-deploy.yml b/Api.Data.Identity/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 89ba45d0..00000000 --- a/Api.Data.Identity/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.Identity - IMAGE_NAME: api.data.identity - SERVICE_NAME: api-data-identity - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Identity/.gitignore b/Api.Data.Identity/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.Identity/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Identity/.kubernetes/autoscaler.yaml b/Api.Data.Identity/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.Identity/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Identity/.kubernetes/configmap.yaml b/Api.Data.Identity/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.Identity/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Identity/.kubernetes/deployment.yaml b/Api.Data.Identity/.kubernetes/deployment.yaml deleted file mode 100644 index 5901ef66..00000000 --- a/Api.Data.Identity/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Identity/.kubernetes/service.yaml b/Api.Data.Identity/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.Identity/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Properties/DoNotParallelize.cs b/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Tests.Api.Data.Identity.csproj b/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Tests.Api.Data.Identity.csproj deleted file mode 100644 index 449cc3ac..00000000 --- a/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Tests.Api.Data.Identity.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj b/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity.Models/Criterias/UserQueryCriteria.cs b/Api.Data.Identity/Api.Data.Identity.Models/Criterias/UserQueryCriteria.cs deleted file mode 100644 index d3bdb438..00000000 --- a/Api.Data.Identity/Api.Data.Identity.Models/Criterias/UserQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; -using System.Collections.Generic; -using System.Linq; - -namespace Api.Data.Identity.Models.Criterias; - -/// -public class UserQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(User.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity.Models/User.cs b/Api.Data.Identity/Api.Data.Identity.Models/User.cs deleted file mode 100644 index ada9f5d6..00000000 --- a/Api.Data.Identity/Api.Data.Identity.Models/User.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Identity.Models; - -/// -/// User. -/// -public class User : BaseEntityUser -{ - /// - /// Name. - /// - [Required] - [MaxLength(128)] - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity.sln b/Api.Data.Identity/Api.Data.Identity.sln deleted file mode 100644 index f87402ac..00000000 --- a/Api.Data.Identity/Api.Data.Identity.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Models", "Api.Data.Identity.Models\Api.Data.Identity.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity", "Api.Data.Identity\Api.Data.Identity.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Identity", ".tests\Tests.Api.Data.Identity\Tests.Api.Data.Identity.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.Identity/Api.Data.Identity/Api.Data.Identity.csproj b/Api.Data.Identity/Api.Data.Identity/Api.Data.Identity.csproj deleted file mode 100644 index 308426b0..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Api.Data.Identity.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Controllers/UsersController.cs b/Api.Data.Identity/Api.Data.Identity/Controllers/UsersController.cs deleted file mode 100644 index 6d14a4cc..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Controllers/UsersController.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Api.Data.Identity.Models; -using Api.Data.Identity.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; -using Nano.Data.Abstractions.Identity; - -namespace Api.Data.Identity.Controllers; - -/// -public class UsersController(ILogger logger, IRepository repository, IIdentityRepository identityRepository) - : BaseEntityUserController(logger, repository, identityRepository); \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Data/Mappings/UserMapping.cs b/Api.Data.Identity/Api.Data.Identity/Data/Mappings/UserMapping.cs deleted file mode 100644 index e910480b..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Data/Mappings/UserMapping.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Api.Data.Identity.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings.Identity; - -namespace Api.Data.Identity.Data.Mappings; - -/// -public class UserMapping : BaseEntityUserMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - if (builder == null) - throw new ArgumentNullException(nameof(builder)); - - base.Configure(builder); - - builder - .Property(x => x.Name) - .HasMaxLength(128) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContext.cs b/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContext.cs deleted file mode 100644 index 715d283c..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.Identity.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContextFactory.cs b/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContextFactory.cs deleted file mode 100644 index db58e2c0..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.Identity.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local b/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local deleted file mode 100644 index a05f0920..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.Identity.dll"] \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.Designer.cs b/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.Designer.cs deleted file mode 100644 index b46e5b7a..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.Designer.cs +++ /dev/null @@ -1,660 +0,0 @@ -// -using System; -using Api.Data.Identity.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Identity.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415142847_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Identity.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Identity.Models.User", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Api.Data.Identity.Models.User", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.cs b/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.cs deleted file mode 100644 index 5bda95ad..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.Identity.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "User", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_User", x => x.Id); - table.ForeignKey( - name: "FK_User___EFIdentityUser_Id", - column: x => x.Id, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_User_CreatedAt", - table: "User", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_User_IsDeleted", - table: "User", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "User"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.Identity/Api.Data.Identity/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Identity/Api.Data.Identity/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 0478fbe9..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,657 +0,0 @@ -// -using System; -using Api.Data.Identity.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Identity.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Identity.Models.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("User"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Identity.Models.User", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Api.Data.Identity.Models.User", "Id") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Identity/Api.Data.Identity/Program.cs b/Api.Data.Identity/Api.Data.Identity/Program.cs deleted file mode 100644 index d4ab1f42..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.Identity.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.Identity/Api.Data.Identity/Properties/InternalsVisibleTo.cs b/Api.Data.Identity/Api.Data.Identity/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 4da322e5..00000000 --- a/Api.Data.Identity/Api.Data.Identity/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.Identity")] \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json b/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.Production.json b/Api.Data.Identity/Api.Data.Identity/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Identity/Api.Data.Identity/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.Staging.json b/Api.Data.Identity/Api.Data.Identity/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Identity/Api.Data.Identity/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.json b/Api.Data.Identity/Api.Data.Identity/appsettings.json deleted file mode 100644 index 1e2f21b9..00000000 --- a/Api.Data.Identity/Api.Data.Identity/appsettings.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Documentation": { - } - }, - "Data": { - "ConnectionString": null, - "Identity": { - "TokensExpiration": "24:00:00", - "UseAudit": "All", - "User": { - "IsUniqueEmailAddressRequired": true, - "IsUniquePhoneNumberRequired": false, - "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", - "DefaultRoles": [ - "administrator" - ] - }, - "SignIn": { - "RequireConfirmedEmail": false, - "RequireConfirmedPhoneNumber": false - }, - "Lockout": { - "AllowedForNewUsers": true, - "MaxFailedAccessAttempts": 3, - "DefaultLockoutTimeSpan": "00:30:00" - }, - "Password": { - "RequireDigit": true, - "RequireNonAlphanumeric": true, - "RequireLowercase": true, - "RequirUppercase": true, - "RequiredLength": 12, - "RequiredUniqueCharacters": 3 - } - } - } -} \ No newline at end of file diff --git a/Api.Data.Identity/Dockerfile b/Api.Data.Identity/Dockerfile deleted file mode 100644 index 0e6712aa..00000000 --- a/Api.Data.Identity/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.Identity.dll"] \ No newline at end of file diff --git a/Api.Data.Identity/LICENSE b/Api.Data.Identity/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.Identity/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Identity/README.md b/Api.Data.Identity/README.md deleted file mode 100644 index 0baad855..00000000 --- a/Api.Data.Identity/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# Api.Data.Identity - -> _Nano API application with data identity._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to -demonstrate repository autosave. Entity controllers have been simplified to showcase identity; full controllers are unnecessary. - -The `User` entity model, `UserMapping` data mapping, `UserQueryCriteria` query criteria and the `UsersControlller` has been added to the solutiin. The controller is deriving -from the `BaseIdentityContrlller`, epxosing all configured identity actions. Everything needed to expose identity controller actions. - -The application is configured to audit all identity models. - -Also, API documentation has been configured, in order to easier see which audit endpoints are available. It can be accessed -here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -Not all identity actions are enabled in this example. It demonstrates identity functionality without any authentication enabled. Other lessons cover the different supported -authentication methods and the corresponding identity actions to manage them. - -> 📖 Learn more about **[Nano Data Identity](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#identity)**. - -## Configuration -The data identity has been configured for the application. The `UseAudit` ahs been set to `All` in order to audit log all identity changes. Normally, you would probably be more -selective in that option and choose some identityt model to audit. - -```json -"Data": { - "ConnectionString": null, - "Identity": { - "TokensExpiration": "24:00:00", - "UseAudit": "All", - "User": { - "IsUniqueEmailAddressRequired": true, - "IsUniquePhoneNumberRequired": false, - "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", - "DefaultRoles": [ - "administrator" - ] - }, - "SignIn": { - "RequireConfirmedEmail": false, - "RequireConfirmedPhoneNumber": false - }, - "Lockout": { - "AllowedForNewUsers": true, - "MaxFailedAccessAttempts": 3, - "DefaultLockoutTimeSpan": "00:30:00" - }, - "Password": { - "RequireDigit": true, - "RequireNonAlphanumeric": true, - "RequireLowercase": true, - "RequirUppercase": true, - "RequiredLength": 12, - "RequiredUniqueCharacters": 3 - } - } -} -``` diff --git a/Api.Data.Identity/icon.png b/Api.Data.Identity/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.InMemory/.docker/docker-compose.yml b/Api.Data.InMemory/.docker/docker-compose.yml deleted file mode 100644 index 6a0d4084..00000000 --- a/Api.Data.InMemory/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.data.inmemory: - image: api.data.inmemory - hostname: api-data-inmemory - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.InMemory - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.InMemory/.dockerignore b/Api.Data.InMemory/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.InMemory/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.InMemory/.github/config/slack.yml b/Api.Data.InMemory/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.InMemory/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.InMemory/.github/workflows/build-and-deploy.yml b/Api.Data.InMemory/.github/workflows/build-and-deploy.yml deleted file mode 100644 index fa3b6652..00000000 --- a/Api.Data.InMemory/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.InMemory - IMAGE_NAME: api.data.inmemory - SERVICE_NAME: api-data-inmemory - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.InMemory/.gitignore b/Api.Data.InMemory/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.InMemory/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.InMemory/.kubernetes/autoscaler.yaml b/Api.Data.InMemory/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.InMemory/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.InMemory/.kubernetes/configmap.yaml b/Api.Data.InMemory/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.InMemory/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.InMemory/.kubernetes/deployment.yaml b/Api.Data.InMemory/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Data.InMemory/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.InMemory/.kubernetes/service.yaml b/Api.Data.InMemory/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.InMemory/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Properties/DoNotParallelize.cs b/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Tests.Api.Data.InMemory.csproj b/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Tests.Api.Data.InMemory.csproj deleted file mode 100644 index 1fab9bfa..00000000 --- a/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Tests.Api.Data.InMemory.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj b/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj deleted file mode 100644 index f949c7de..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 859c7482..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.InMemory.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith("Name", this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/Example.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/Example.cs deleted file mode 100644 index 5bd8e646..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.InMemory.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatable.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatable.cs deleted file mode 100644 index 14f5fe12..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.InMemory.Models; - -/// -/// Example. -/// -public class ExampleCreatable : BaseEntityCreatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatableAndEditable.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatableAndEditable.cs deleted file mode 100644 index 015ffeef..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatableAndEditable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.InMemory.Models; - -/// -/// Example. -/// -public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleDeletable.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleDeletable.cs deleted file mode 100644 index 29dd2690..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleDeletable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.InMemory.Models; - -/// -/// Example. -/// -public class ExampleDeletable : BaseEntityDeletable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleUpdatable.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleUpdatable.cs deleted file mode 100644 index 13d2e0e5..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleUpdatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.InMemory.Models; - -/// -/// Example. -/// -public class ExampleUpdatable : BaseEntityUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.sln b/Api.Data.InMemory/Api.Data.InMemory.sln deleted file mode 100644 index c33a403f..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.InMemory.Models", "Api.Data.InMemory.Models\Api.Data.InMemory.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.InMemory", "Api.Data.InMemory\Api.Data.InMemory.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.InMemory", ".tests\Tests.Api.Data.InMemory\Tests.Api.Data.InMemory.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.InMemory", "..\..\Nano.Library\Nano.Data.InMemory\Nano.Data.InMemory.csproj", "{C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.InMemory/Api.Data.InMemory/Api.Data.InMemory.csproj b/Api.Data.InMemory/Api.Data.InMemory/Api.Data.InMemory.csproj deleted file mode 100644 index 90828eef..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Api.Data.InMemory.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreatableAndEditableController.cs deleted file mode 100644 index ac30d52f..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreatableAndEditableController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.InMemory.Models; -using Api.Data.InMemory.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.InMemory.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) - : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreateablesController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreateablesController.cs deleted file mode 100644 index cf47b529..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreateablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.InMemory.Models; -using Api.Data.InMemory.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.InMemory.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreateablesController(ILogger logger, IRepository repository) - : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleDeletablesController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleDeletablesController.cs deleted file mode 100644 index d0c2d534..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleDeletablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.InMemory.Models; -using Api.Data.InMemory.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.InMemory.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleDeletablesController(ILogger logger, IRepository repository) - : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleUpdatablesController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleUpdatablesController.cs deleted file mode 100644 index e0a3b5b4..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleUpdatablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.InMemory.Models; -using Api.Data.InMemory.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.InMemory.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleUpdatablesController(ILogger logger, IRepository repository) - : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExamplesController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExamplesController.cs deleted file mode 100644 index 2c17e281..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.InMemory.Models; -using Api.Data.InMemory.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.InMemory.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/InMemoryDbContext.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/InMemoryDbContext.cs deleted file mode 100644 index 661424d5..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Data/InMemoryDbContext.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; -using Nano.Eventing.Abstractions; - -namespace Api.Data.InMemory.Data; - -/// -public class InMemoryDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs deleted file mode 100644 index de1b56ff..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.InMemory.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.InMemory.Data.Mappings; - -/// -/// Example Creatable And Updatable Mapping. -/// -public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableMapping.cs deleted file mode 100644 index cec926ea..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.InMemory.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.InMemory.Data.Mappings; - -/// -/// Example Creatable Mapping. -/// -public class ExampleCreatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleDeletableMapping.cs deleted file mode 100644 index 864e4dbf..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleDeletableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.InMemory.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.InMemory.Data.Mappings; - -/// -/// Example Deletable Mapping. -/// -public class ExampleDeletableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 11696401..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.InMemory.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.InMemory.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleUpdatableMapping.cs deleted file mode 100644 index 67221228..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.InMemory.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.InMemory.Data.Mappings; - -/// -/// Example Updatable Mapping. -/// -public class ExampleUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local b/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local deleted file mode 100644 index a61be1c9..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.InMemory.dll"] \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Program.cs b/Api.Data.InMemory/Api.Data.InMemory/Program.cs deleted file mode 100644 index d003f412..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.InMemory.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.InMemory; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.InMemory/Api.Data.InMemory/Properties/InternalsVisibleTo.cs b/Api.Data.InMemory/Api.Data.InMemory/Properties/InternalsVisibleTo.cs deleted file mode 100644 index e3a05a6c..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.InMemory")] \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Development.json b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Production.json b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Staging.json b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/appsettings.json b/Api.Data.InMemory/Api.Data.InMemory/appsettings.json deleted file mode 100644 index 55288f2b..00000000 --- a/Api.Data.InMemory/Api.Data.InMemory/appsettings.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Documentation": { - "Name": "Application" - } - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "StartupAction": "None", - "UseLazyLoading": false, - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": "nanoDb", - "Repository": { - "UseAutoSave": true, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } -} \ No newline at end of file diff --git a/Api.Data.InMemory/Dockerfile b/Api.Data.InMemory/Dockerfile deleted file mode 100644 index ff662879..00000000 --- a/Api.Data.InMemory/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.InMemory.dll"] \ No newline at end of file diff --git a/Api.Data.InMemory/LICENSE b/Api.Data.InMemory/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.InMemory/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.InMemory/README.md b/Api.Data.InMemory/README.md deleted file mode 100644 index 8ecfb045..00000000 --- a/Api.Data.InMemory/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Api.Data.InMemory - -> _Nano API application with in-memory data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from -the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). As the in-memory data provider doesn't use migrations there is no need -to implement the `BaseDbContextFactory`. - -Additionally, the example shows how Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories) works along with the corresponding -entity controllers. For more information on controllers and how they are connected with entity models, see [Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#controllers). - -Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed -here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing -the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. - -> 📖 Learn more about **[Nano.Data.InMemory](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.InMemory)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "StartupAction": "None", - "UseLazyLoading": false, - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": "nanoDb", - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null -} -``` diff --git a/Api.Data.InMemory/icon.png b/Api.Data.InMemory/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.LazyLoading/.docker/docker-compose.yml b/Api.Data.LazyLoading/.docker/docker-compose.yml deleted file mode 100644 index b31fee18..00000000 --- a/Api.Data.LazyLoading/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.lazyloading: - image: api.data.lazyloading - hostname: api-data-lazyloading - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.LazyLoading - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.LazyLoading/.dockerignore b/Api.Data.LazyLoading/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.LazyLoading/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.LazyLoading/.github/config/slack.yml b/Api.Data.LazyLoading/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.LazyLoading/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml b/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml deleted file mode 100644 index b831b671..00000000 --- a/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.LazyLoading - IMAGE_NAME: api.data.lazyloading - SERVICE_NAME: api-data-lazyloading - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.LazyLoading/.gitignore b/Api.Data.LazyLoading/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.LazyLoading/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.LazyLoading/.kubernetes/autoscaler.yaml b/Api.Data.LazyLoading/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.LazyLoading/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.LazyLoading/.kubernetes/configmap.yaml b/Api.Data.LazyLoading/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.LazyLoading/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.LazyLoading/.kubernetes/deployment.yaml b/Api.Data.LazyLoading/.kubernetes/deployment.yaml deleted file mode 100644 index 5901ef66..00000000 --- a/Api.Data.LazyLoading/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.LazyLoading/.kubernetes/service.yaml b/Api.Data.LazyLoading/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.LazyLoading/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Properties/DoNotParallelize.cs b/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Tests.Api.Data.LazyLoading.csproj b/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Tests.Api.Data.LazyLoading.csproj deleted file mode 100644 index 451cc28d..00000000 --- a/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Tests.Api.Data.LazyLoading.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 9882acfe..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.LazyLoading.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Example.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Example.cs deleted file mode 100644 index d3af35ef..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Example.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using Nano.Data.Abstractions.Annotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.LazyLoading.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; - - /// - /// Relations. - /// - public virtual ICollection Relations { get; set; } = []; - - /// - /// Relations. - /// - [Include] - public virtual ICollection IncludedRelations { get; set; } = []; -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelation.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelation.cs deleted file mode 100644 index 4a900e8b..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelation.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.LazyLoading.Models; - -/// -/// Example Relation. -/// -public class ExampleRelation : BaseEntity -{ - /// - /// Example. - /// - [Required] - public virtual Guid ExampleId { get; set; } - - /// - /// Example. - /// - public virtual Example? Example { get; set; } = null!; - - /// - /// Text. - /// - public virtual string? Text { get; set; } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelationIncluded.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelationIncluded.cs deleted file mode 100644 index 521bd1ff..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelationIncluded.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.LazyLoading.Models; - -/// -/// Example Relation Included. -/// -public class ExampleRelationIncluded : BaseEntity -{ - /// - /// Example. - /// - [Required] - public virtual Guid ExampleId { get; set; } - - /// - /// Example. - /// - public virtual Example? Example { get; set; } = null!; - - /// - /// Text. - /// - public virtual string? Text { get; set; } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.sln b/Api.Data.LazyLoading/Api.Data.LazyLoading.sln deleted file mode 100644 index d6e19f30..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.LazyLoading.Models", "Api.Data.LazyLoading.Models\Api.Data.LazyLoading.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.LazyLoading", "Api.Data.LazyLoading\Api.Data.LazyLoading.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.LazyLoading", ".tests\Tests.Api.Data.LazyLoading\Tests.Api.Data.LazyLoading.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Api.Data.LazyLoading.csproj b/Api.Data.LazyLoading/Api.Data.LazyLoading/Api.Data.LazyLoading.csproj deleted file mode 100644 index 6b91d373..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Api.Data.LazyLoading.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Controllers/ExamplesController.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Controllers/ExamplesController.cs deleted file mode 100644 index f3a81a91..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Controllers/ExamplesController.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.Data.LazyLoading.Models; -using Api.Data.LazyLoading.Models.Criterias; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.LazyLoading.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository) -{ - /// - /// Lazy Loading Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("lazy-loading")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LazyLoadingAsync(CancellationToken cancellationToken = default) - { - var example = await this.Repository - .GetFirstAsync(x => true, cancellationToken); - - if (example == null) - { - return this.NotFound(); - } - - var lazyloadedRelations = example.Relations; - var lazyloadedIncludedRelations = example.IncludedRelations; - - return this.Ok(example); - } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleMapping.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 0c49d1c8..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using Api.Data.LazyLoading.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.LazyLoading.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - - builder - .HasMany(x => x.Relations) - .WithOne(x => x.Example) - .IsRequired(); - - builder - .HasMany(x => x.IncludedRelations) - .WithOne(x => x.Example) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationIncludedMapping.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationIncludedMapping.cs deleted file mode 100644 index fc03feb5..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationIncludedMapping.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Api.Data.LazyLoading.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.LazyLoading.Data.Mappings; - -/// -/// Example Relation Mapping. -/// -public class ExampleRelationIncludedMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .HasOne(x => x.Example) - .WithMany(x => x.IncludedRelations) - .IsRequired(); - - builder - .Property(x => x.Text); - } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationMapping.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationMapping.cs deleted file mode 100644 index 7fbc34e4..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationMapping.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Api.Data.LazyLoading.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.LazyLoading.Data.Mappings; - -/// -/// Example Relation Mapping. -/// -public class ExampleRelationMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .HasOne(x => x.Example) - .WithMany(x => x.Relations) - .IsRequired(); - - builder - .Property(x => x.Text); - } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContext.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContext.cs deleted file mode 100644 index 3874f906..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.LazyLoading.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContextFactory.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContextFactory.cs deleted file mode 100644 index d68db571..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.LazyLoading.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local b/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local deleted file mode 100644 index ad93161d..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.LazyLoading.dll"] \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.Designer.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.Designer.cs deleted file mode 100644 index 02d6ffb2..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.Designer.cs +++ /dev/null @@ -1,748 +0,0 @@ -// -using System; -using Api.Data.LazyLoading.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.LazyLoading.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415143541_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("ExampleId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Text") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("ExampleId"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleRelation"); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelationIncluded", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("ExampleId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Text") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("ExampleId"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleRelationIncluded"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelation", b => - { - b.HasOne("Api.Data.LazyLoading.Models.Example", "Example") - .WithMany("Relations") - .HasForeignKey("ExampleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Example"); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelationIncluded", b => - { - b.HasOne("Api.Data.LazyLoading.Models.Example", "Example") - .WithMany("IncludedRelations") - .HasForeignKey("ExampleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.Example", b => - { - b.Navigation("IncludedRelations"); - - b.Navigation("Relations"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.cs deleted file mode 100644 index efafb878..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.cs +++ /dev/null @@ -1,685 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.LazyLoading.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleRelation", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ExampleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Text = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleRelation", x => x.Id); - table.ForeignKey( - name: "FK_ExampleRelation_Example_ExampleId", - column: x => x.ExampleId, - principalTable: "Example", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleRelationIncluded", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ExampleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Text = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleRelationIncluded", x => x.Id); - table.ForeignKey( - name: "FK_ExampleRelationIncluded_Example_ExampleId", - column: x => x.ExampleId, - principalTable: "Example", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleRelation_CreatedAt", - table: "ExampleRelation", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleRelation_ExampleId", - table: "ExampleRelation", - column: "ExampleId"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleRelation_IsDeleted", - table: "ExampleRelation", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleRelationIncluded_CreatedAt", - table: "ExampleRelationIncluded", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleRelationIncluded_ExampleId", - table: "ExampleRelationIncluded", - column: "ExampleId"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleRelationIncluded_IsDeleted", - table: "ExampleRelationIncluded", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "ExampleRelation"); - - migrationBuilder.DropTable( - name: "ExampleRelationIncluded"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 115bdf5b..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,745 +0,0 @@ -// -using System; -using Api.Data.LazyLoading.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.LazyLoading.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("ExampleId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Text") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("ExampleId"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleRelation"); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelationIncluded", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("ExampleId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Text") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("ExampleId"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleRelationIncluded"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelation", b => - { - b.HasOne("Api.Data.LazyLoading.Models.Example", "Example") - .WithMany("Relations") - .HasForeignKey("ExampleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Example"); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelationIncluded", b => - { - b.HasOne("Api.Data.LazyLoading.Models.Example", "Example") - .WithMany("IncludedRelations") - .HasForeignKey("ExampleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Api.Data.LazyLoading.Models.Example", b => - { - b.Navigation("IncludedRelations"); - - b.Navigation("Relations"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Program.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Program.cs deleted file mode 100644 index 296b2db6..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.LazyLoading.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Properties/InternalsVisibleTo.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 3e44d1d2..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.LazyLoading")] \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Production.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Staging.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.json deleted file mode 100644 index 73c4d65a..00000000 --- a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null, - "UseLazyLoading": true - } -} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Dockerfile b/Api.Data.LazyLoading/Dockerfile deleted file mode 100644 index 14732284..00000000 --- a/Api.Data.LazyLoading/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.LazyLoading.dll"] \ No newline at end of file diff --git a/Api.Data.LazyLoading/LICENSE b/Api.Data.LazyLoading/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.LazyLoading/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.LazyLoading/README.md b/Api.Data.LazyLoading/README.md deleted file mode 100644 index 518f7972..00000000 --- a/Api.Data.LazyLoading/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Api.Data.LazyLoading - -> _Nano API application with data lazy loading._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to -demonstrate repository autosave. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. - -Once the object graph is created, notice that only `IncludedRelations` appears in the response. Although `Relations` is lazy-loaded in the code, it is not included -because it lacks the `Include` annotation. - -> 📖 Learn more about **[Nano Data Lazy Loading](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#lazy-loading)**. - -## Configuration -```json -"Data": { - "UseLazyLoading": true -} -``` diff --git a/Api.Data.LazyLoading/icon.png b/Api.Data.LazyLoading/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Collation/.docker/docker-compose.yml b/Api.Data.MySql.Collation/.docker/docker-compose.yml deleted file mode 100644 index edbf3b37..00000000 --- a/Api.Data.MySql.Collation/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.mysql.collation: - image: api.data.mysql.collation - hostname: api-data-mysql-collation - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.MySql.Collation - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.MySql.Collation/.dockerignore b/Api.Data.MySql.Collation/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.MySql.Collation/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.MySql.Collation/.github/config/slack.yml b/Api.Data.MySql.Collation/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.MySql.Collation/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 514b42df..00000000 --- a/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.MySql.Collation - IMAGE_NAME: api.data.mysql.collation - SERVICE_NAME: api-data-mysql-collation - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/.gitignore b/Api.Data.MySql.Collation/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.MySql.Collation/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.Collation/.kubernetes/autoscaler.yaml b/Api.Data.MySql.Collation/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.MySql.Collation/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.Collation/.kubernetes/configmap.yaml b/Api.Data.MySql.Collation/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.MySql.Collation/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.Collation/.kubernetes/deployment.yaml b/Api.Data.MySql.Collation/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.MySql.Collation/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.Collation/.kubernetes/service.yaml b/Api.Data.MySql.Collation/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.MySql.Collation/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Properties/DoNotParallelize.cs b/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Tests.Api.Data.MySql.Collation.csproj b/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Tests.Api.Data.MySql.Collation.csproj deleted file mode 100644 index af89191c..00000000 --- a/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Tests.Api.Data.MySql.Collation.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 9020b2bb..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.MySql.Collation.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Example.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Example.cs deleted file mode 100644 index 8044c6eb..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Collation.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.sln b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.sln deleted file mode 100644 index e2a37955..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Collation.Models", "Api.Data.MySql.Collation.Models\Api.Data.MySql.Collation.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Collation", "Api.Data.MySql.Collation\Api.Data.MySql.Collation.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.Collation", ".tests\Tests.Api.Data.MySql.Collation\Tests.Api.Data.MySql.Collation.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Api.Data.MySql.Collation.csproj b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Api.Data.MySql.Collation.csproj deleted file mode 100644 index 36f2c8c0..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Api.Data.MySql.Collation.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Controllers/ExamplesController.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Controllers/ExamplesController.cs deleted file mode 100644 index 22947a2c..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Collation.Models; -using Api.Data.MySql.Collation.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Collation.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 670874df..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.MySql.Collation.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Collation.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContext.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContext.cs deleted file mode 100644 index 1acf9a1e..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContext.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; -using Nano.Eventing.Abstractions; - -namespace Api.Data.MySql.Collation.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 55b48f03..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.MySql.Collation.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local deleted file mode 100644 index f2716c69..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.MySql.Collation.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.Designer.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.Designer.cs deleted file mode 100644 index dccc1a17..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.Designer.cs +++ /dev/null @@ -1,651 +0,0 @@ -// -using System; -using Api.Data.MySql.Collation.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Collation.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415143932_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Collation.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.cs deleted file mode 100644 index 4cf37a7a..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.MySql.Collation.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 11b521cf..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,648 +0,0 @@ -// -using System; -using Api.Data.MySql.Collation.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Collation.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Collation.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Program.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Program.cs deleted file mode 100644 index ff90c81e..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.MySql.Collation.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 32060844..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.Collation")] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Production.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Staging.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json deleted file mode 100644 index d43a066a..00000000 --- a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Dockerfile b/Api.Data.MySql.Collation/Dockerfile deleted file mode 100644 index 0d24bca4..00000000 --- a/Api.Data.MySql.Collation/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.MySql.Collation.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/LICENSE b/Api.Data.MySql.Collation/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.MySql.Collation/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.Collation/README.md b/Api.Data.MySql.Collation/README.md deleted file mode 100644 index cf09de59..00000000 --- a/Api.Data.MySql.Collation/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Api.Data.MySql.Collation - -> _Nano API application with mysql collation data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been -simplified to showcase setting mysql default collation; full controllers are unnecessary. - -This example demonstrates setting the `DefaultCollation` in the `Data` section of the configuration. Notice that querying `Example.Name` with -a case-insensitive collation returns results regardless of letter casing. - -⚠️ Note: Changing this setting affects only new migrations and will not modify existing tables or columns. - -> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql)**. - -## Configuration -The collation is set in `appsettings`. - -```json -"Data": { - "DefaultCollation": "utf8mb4_general_ci" -} -``` \ No newline at end of file diff --git a/Api.Data.MySql.Collation/icon.png b/Api.Data.MySql.Collation/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/.docker/docker-compose.yml b/Api.Data.MySql.Mappings/.docker/docker-compose.yml deleted file mode 100644 index 7fe5c2e8..00000000 --- a/Api.Data.MySql.Mappings/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.mysql.mappings: - image: api.data.mysql.mappings - hostname: api-data-mysql-mappings - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.MySql.Mappings - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.MySql.Mappings/.dockerignore b/Api.Data.MySql.Mappings/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.MySql.Mappings/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.MySql.Mappings/.github/config/slack.yml b/Api.Data.MySql.Mappings/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.MySql.Mappings/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml deleted file mode 100644 index ed044ed6..00000000 --- a/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.MySql.Mappings - IMAGE_NAME: api.data.mysql.mappings - SERVICE_NAME: api-data-mysql-mappings - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/.gitignore b/Api.Data.MySql.Mappings/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.MySql.Mappings/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/.kubernetes/autoscaler.yaml b/Api.Data.MySql.Mappings/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.MySql.Mappings/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml b/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml b/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.Mappings/.kubernetes/service.yaml b/Api.Data.MySql.Mappings/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.MySql.Mappings/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Properties/DoNotParallelize.cs b/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Tests.Api.Data.MySql.Mappings.csproj b/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Tests.Api.Data.MySql.Mappings.csproj deleted file mode 100644 index 20af7932..00000000 --- a/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Tests.Api.Data.MySql.Mappings.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleNormalizedQueryCriteria.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleNormalizedQueryCriteria.cs deleted file mode 100644 index d741ec05..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleNormalizedQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.MySql.Mappings.Models.Criterias; - -/// -public class ExampleNormalizedQueryCriteria : BaseQueryCriteria -{ - /// - /// Full Name. - /// - public virtual string? FullName { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.FullName)) - { - expression - .StartsWith(nameof(ExampleNormalized.FullNameNormalized), this.FullName.ToUpper()); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 856b17b2..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.MySql.Mappings.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleJson.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleJson.cs deleted file mode 100644 index 9fdb849e..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleJson.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Api.Data.MySql.Mappings.Models.Types; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Mappings.Models; - -/// -/// Example Json. -/// -public class ExampleJson : BaseEntity -{ - /// - /// Profile As Json. - /// - [Required] - public virtual Profile ProfileAsJson { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleNormalized.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleNormalized.cs deleted file mode 100644 index 7c63aefe..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleNormalized.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Mappings.Models; - -/// -/// Example Normalized. -/// -public class ExampleNormalized : BaseEntity -{ - private string firstName = null!; - private string lastName = null!; - - /// - /// First Name. - /// - [Required] - [MaxLength(128)] - public virtual string FirstName - { - get => this.firstName; - set - { - this.firstName = value; - this.FullName = $"{this.firstName} {this.lastName}"; - } - } - - /// - /// Last Name. - /// - [Required] - [MaxLength(128)] - public virtual string LastName - { - get => this.lastName; - set - { - this.lastName = value; - this.FullName = $"{this.firstName} {this.lastName}"; - } - } - - /// - /// Full Name. - /// - [Required] - [MaxLength(256)] - public virtual string FullName - { - get; - set - { - field = value; - this.FullNameNormalized = value.ToUpper(); - } - } = null!; - - /// - /// Full Name Normalized. - /// - [Required] - [MaxLength(256)] - public virtual string FullNameNormalized { get; internal set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleOwned.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleOwned.cs deleted file mode 100644 index 288b1786..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleOwned.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; -using Api.Data.MySql.Mappings.Models.Types; - -namespace Api.Data.MySql.Mappings.Models; - -/// -/// Example Owned. -/// -public class ExampleOwned : BaseEntity -{ - /// - /// Profile. - /// - [Required] - public virtual Profile Profile { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/Profile.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/Profile.cs deleted file mode 100644 index 985cdbfd..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/Profile.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Api.Data.MySql.Mappings.Models.Types; - -/// -/// Profile. -/// -public class Profile -{ - /// - /// Text. - /// - public virtual string? Text { get; set; } - - /// - /// Picture. - /// - public virtual ProfilePicture? Picture { get; set; } - - /// - /// Settings. - /// - [Required] - public virtual ProfileSettings Settings { get; set; } = new(); -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfilePicture.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfilePicture.cs deleted file mode 100644 index 026650b9..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfilePicture.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Api.Data.MySql.Mappings.Models.Types; - -/// -/// Profile Picture. -/// -public class ProfilePicture -{ - /// - /// Text. - /// - public virtual Guid Id { get; set; } - - /// - /// Text. - /// - public virtual string? Path { get; set; } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfileSettings.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfileSettings.cs deleted file mode 100644 index 23548886..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfileSettings.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; - -namespace Api.Data.MySql.Mappings.Models.Types; - -/// -/// Profile Settings. -/// -public class ProfileSettings -{ - /// - /// Use Dark Mode. - /// - [Required] - [DefaultValue(false)] - public virtual bool UseDarkMode { get; set; } = false; - - /// - /// Hide Profile Name. - /// - [Required] - [DefaultValue(false)] - public virtual bool HideProfileName { get; set; } = false; -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.sln b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.sln deleted file mode 100644 index 365c8723..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Mappings.Models", "Api.Data.MySql.Mappings.Models\Api.Data.MySql.Mappings.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Mappings", "Api.Data.MySql.Mappings\Api.Data.MySql.Mappings.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.Mappings", ".tests\Tests.Api.Data.MySql.Mappings\Tests.Api.Data.MySql.Mappings.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.csproj b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.csproj deleted file mode 100644 index aaf663e6..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleJsonController.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleJsonController.cs deleted file mode 100644 index 60bef305..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleJsonController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; -using Api.Data.MySql.Mappings.Models; -using Api.Data.MySql.Mappings.Models.Criterias; - -namespace Api.Data.MySql.Mappings.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleJsonsController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleNormalizedController.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleNormalizedController.cs deleted file mode 100644 index 46e2bcbf..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleNormalizedController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Mappings.Models; -using Api.Data.MySql.Mappings.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Mappings.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleNormalizedsController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleOwnedController.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleOwnedController.cs deleted file mode 100644 index 8ff3ee89..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleOwnedController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Mappings.Models; -using Api.Data.MySql.Mappings.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Mappings.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleOwnedsController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleJsonMapping.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleJsonMapping.cs deleted file mode 100644 index 785296a2..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleJsonMapping.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Api.Data.MySql.Mappings.Models; -using Api.Data.MySql.Mappings.Models.Types; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; -using Newtonsoft.Json; - -namespace Api.Data.MySql.Mappings.Data.Mappings; - -/// -/// Example Json Mapping. -/// -public class ExampleJsonMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.ProfileAsJson) - .HasConversion( - x => JsonConvert.SerializeObject(x), - x => JsonConvert.DeserializeObject(x)!); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleNormalizedMapping.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleNormalizedMapping.cs deleted file mode 100644 index aa1e45bb..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleNormalizedMapping.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Api.Data.MySql.Mappings.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; -using System; - -namespace Api.Data.MySql.Mappings.Data.Mappings; - -/// -/// Example Normalized Mapping. -/// -public class ExampleNormalizedMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.FirstName) - .HasMaxLength(128) - .IsRequired(); - - builder - .Property(x => x.LastName) - .HasMaxLength(128) - .IsRequired(); - - builder - .Property(x => x.FullName) - .HasMaxLength(256) - .IsRequired(); - - builder - .Property(x => x.FullNameNormalized) - .HasMaxLength(256) - .IsRequired(); - - builder - .HasIndex(x => x.FullNameNormalized) - .IsUnique(); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleOwnedMapping.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleOwnedMapping.cs deleted file mode 100644 index b691511d..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleOwnedMapping.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Api.Data.MySql.Mappings.Data.Mappings.Extensions; -using Api.Data.MySql.Mappings.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Mappings.Data.Mappings; - -/// -/// Example Owned Mapping. -/// -public class ExampleOwnedMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .MapType(x => x.Profile); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/EntityTypeBuilderExtensions.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/EntityTypeBuilderExtensions.cs deleted file mode 100644 index 3dacc445..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/EntityTypeBuilderExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Linq.Expressions; -using Api.Data.MySql.Mappings.Models.Types; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Abstractions.Models.Abstractions; - -namespace Api.Data.MySql.Mappings.Data.Mappings.Extensions; - -/// -/// Entity Type Builder Extensions. -/// -public static class EntityTypeBuilderExtensions -{ - /// - /// Maps as owned by the entity. - /// - /// The entity type. - /// The . - /// The property expression. - public static void MapType(this EntityTypeBuilder builder, Expression> expression) - where TEntity : class, IEntity - { - if (builder == null) - throw new ArgumentNullException(nameof(builder)); - - if (expression == null) - throw new ArgumentNullException(nameof(expression)); - - builder - .OwnsOne(expression) - .Property(x => x.Text); - - builder - .OwnsOne(expression) - .MapType(x => x.Picture); - - builder - .OwnsOne(expression) - .MapType(x => x.Settings); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/OwnedNavigationBuilderExtensions.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/OwnedNavigationBuilderExtensions.cs deleted file mode 100644 index 65e6a37e..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/OwnedNavigationBuilderExtensions.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Linq.Expressions; -using Api.Data.MySql.Mappings.Models.Types; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Api.Data.MySql.Mappings.Data.Mappings.Extensions; - -/// -/// Owned Navigation Builder Extensions. -/// -public static class OwnedNavigationBuilderExtensions -{ - /// - /// Maps for the owned by . - /// - /// The entity type. - /// The related entity type. - /// The . - /// The . - internal static void MapType(this OwnedNavigationBuilder builder, Expression> expression) - where TEntity : class - where TRelatedEntity : class - { - if (builder == null) - throw new ArgumentNullException(nameof(builder)); - - if (expression == null) - throw new ArgumentNullException(nameof(expression)); - - builder - .OwnsOne(expression) - .Property(x => x.Id) - .IsRequired(); - - builder - .OwnsOne(expression) - .Property(x => x.Path); - } - - /// - /// Maps for the owned by . - /// - /// The entity type. - /// The related entity type. - /// The . - /// The . - internal static void MapType(this OwnedNavigationBuilder builder, Expression> expression) - where TEntity : class - where TRelatedEntity : class - { - if (builder == null) - throw new ArgumentNullException(nameof(builder)); - - if (expression == null) - throw new ArgumentNullException(nameof(expression)); - - builder - .OwnsOne(expression) - .Property(x => x.UseDarkMode) - .HasDefaultValue(false) - .IsRequired(); - - builder - .OwnsOne(expression) - .Property(x => x.HideProfileName) - .HasDefaultValue(false) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContext.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContext.cs deleted file mode 100644 index f0d0786b..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.MySql.Mappings.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 719b04a7..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.MySql.Mappings.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local deleted file mode 100644 index 88ec81d2..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.MySql.Mappings.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.Designer.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.Designer.cs deleted file mode 100644 index d4f92177..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.Designer.cs +++ /dev/null @@ -1,794 +0,0 @@ -// -using System; -using Api.Data.MySql.Mappings.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Mappings.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415143933_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleJson", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("ProfileAsJson") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleJson"); - }); - - modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleNormalized", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("FirstName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("FullName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("FullNameNormalized") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("LastName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("FullNameNormalized") - .IsUnique() - .HasDatabaseName("UX_ExampleNormalized_FullNameNormalized"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleNormalized"); - }); - - modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleOwned", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleOwned"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleOwned", b => - { - b.OwnsOne("Api.Data.MySql.Mappings.Models.Types.Profile", "Profile", b1 => - { - b1.Property("ExampleOwnedId") - .HasColumnType("char(36)"); - - b1.Property("Text") - .HasColumnType("longtext"); - - b1.HasKey("ExampleOwnedId"); - - b1.ToTable("ExampleOwned"); - - b1.WithOwner() - .HasForeignKey("ExampleOwnedId"); - - b1.OwnsOne("Api.Data.MySql.Mappings.Models.Types.ProfilePicture", "Picture", b2 => - { - b2.Property("ProfileExampleOwnedId") - .HasColumnType("char(36)"); - - b2.Property("Id") - .HasColumnType("char(36)"); - - b2.Property("Path") - .HasColumnType("longtext"); - - b2.HasKey("ProfileExampleOwnedId"); - - b2.ToTable("ExampleOwned"); - - b2.WithOwner() - .HasForeignKey("ProfileExampleOwnedId"); - }); - - b1.OwnsOne("Api.Data.MySql.Mappings.Models.Types.ProfileSettings", "Settings", b2 => - { - b2.Property("ProfileExampleOwnedId") - .HasColumnType("char(36)"); - - b2.Property("HideProfileName") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(false); - - b2.Property("UseDarkMode") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(false); - - b2.HasKey("ProfileExampleOwnedId"); - - b2.ToTable("ExampleOwned"); - - b2.WithOwner() - .HasForeignKey("ProfileExampleOwnedId"); - }); - - b1.Navigation("Picture"); - - b1.Navigation("Settings") - .IsRequired(); - }); - - b.Navigation("Profile") - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.cs deleted file mode 100644 index ad41219f..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.cs +++ /dev/null @@ -1,673 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.MySql.Mappings.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleJson", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ProfileAsJson = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleJson", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleNormalized", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - FirstName = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - LastName = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - FullName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - FullNameNormalized = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleNormalized", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleOwned", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Profile_Text = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Profile_Picture_Id = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), - Profile_Picture_Path = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Profile_Settings_UseDarkMode = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - Profile_Settings_HideProfileName = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleOwned", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleJson_CreatedAt", - table: "ExampleJson", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleJson_IsDeleted", - table: "ExampleJson", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleNormalized_CreatedAt", - table: "ExampleNormalized", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleNormalized_IsDeleted", - table: "ExampleNormalized", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "UX_ExampleNormalized_FullNameNormalized", - table: "ExampleNormalized", - column: "FullNameNormalized", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_ExampleOwned_CreatedAt", - table: "ExampleOwned", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleOwned_IsDeleted", - table: "ExampleOwned", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "ExampleJson"); - - migrationBuilder.DropTable( - name: "ExampleNormalized"); - - migrationBuilder.DropTable( - name: "ExampleOwned"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index fbb4bbd7..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,791 +0,0 @@ -// -using System; -using Api.Data.MySql.Mappings.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Mappings.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleJson", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("ProfileAsJson") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleJson"); - }); - - modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleNormalized", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("FirstName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.Property("FullName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("FullNameNormalized") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("LastName") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("varchar(128)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("FullNameNormalized") - .IsUnique() - .HasDatabaseName("UX_ExampleNormalized_FullNameNormalized"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleNormalized"); - }); - - modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleOwned", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleOwned"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleOwned", b => - { - b.OwnsOne("Api.Data.MySql.Mappings.Models.Types.Profile", "Profile", b1 => - { - b1.Property("ExampleOwnedId") - .HasColumnType("char(36)"); - - b1.Property("Text") - .HasColumnType("longtext"); - - b1.HasKey("ExampleOwnedId"); - - b1.ToTable("ExampleOwned"); - - b1.WithOwner() - .HasForeignKey("ExampleOwnedId"); - - b1.OwnsOne("Api.Data.MySql.Mappings.Models.Types.ProfilePicture", "Picture", b2 => - { - b2.Property("ProfileExampleOwnedId") - .HasColumnType("char(36)"); - - b2.Property("Id") - .HasColumnType("char(36)"); - - b2.Property("Path") - .HasColumnType("longtext"); - - b2.HasKey("ProfileExampleOwnedId"); - - b2.ToTable("ExampleOwned"); - - b2.WithOwner() - .HasForeignKey("ProfileExampleOwnedId"); - }); - - b1.OwnsOne("Api.Data.MySql.Mappings.Models.Types.ProfileSettings", "Settings", b2 => - { - b2.Property("ProfileExampleOwnedId") - .HasColumnType("char(36)"); - - b2.Property("HideProfileName") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(false); - - b2.Property("UseDarkMode") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(false); - - b2.HasKey("ProfileExampleOwnedId"); - - b2.ToTable("ExampleOwned"); - - b2.WithOwner() - .HasForeignKey("ProfileExampleOwnedId"); - }); - - b1.Navigation("Picture"); - - b1.Navigation("Settings") - .IsRequired(); - }); - - b.Navigation("Profile") - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Program.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Program.cs deleted file mode 100644 index f97f43b4..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.MySql.Mappings.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 36bba3c1..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.Mappings")] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Production.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Staging.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.json deleted file mode 100644 index d43a066a..00000000 --- a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Dockerfile b/Api.Data.MySql.Mappings/Dockerfile deleted file mode 100644 index bd6047d9..00000000 --- a/Api.Data.MySql.Mappings/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.MySql.Mappings.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/LICENSE b/Api.Data.MySql.Mappings/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.MySql.Mappings/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/README.md b/Api.Data.MySql.Mappings/README.md deleted file mode 100644 index 9f604cf4..00000000 --- a/Api.Data.MySql.Mappings/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Api.Data.MySql.Mappings - -> _Nano API application with mysql advanced data mappings._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been -simplified to showcase setting mysql views; full controllers are unnecessary. - -Three new entity models have been added, each demonstrating different types of advanced mappings. The first, `ExampleJson`, shows how to store a complex object in a -text column: it is serialized when added or updated in the database and deserialized when retrieved, mapping back into the complex object. The second, `ExampleOwned`, -also has a `Profile` reference like `ExampleJson`, but the complex object is stored as **owned entities** within the same table. The third, `ExampleNormalized`, -demonstrates property normalization for querying: `FirstName` and `LastName` are concatenated into `FullName`, and an uppercase version, `FullNameNormalized`, is -used for case-insensitive searches, with the LINQ query calling `.ToUpper()` on the search value to match the normalized property efficiently. - -Last, a unique index has also been added to `Example.NameNormalized`. Observe how Nano renames the index prefixing with 'UX_'. - -> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql)**. diff --git a/Api.Data.MySql.Mappings/icon.png b/Api.Data.MySql.Mappings/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/.docker/docker-compose.yml b/Api.Data.MySql.Spatial/.docker/docker-compose.yml deleted file mode 100644 index 309848ae..00000000 --- a/Api.Data.MySql.Spatial/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.mysql.spatial: - image: api.data.mysql.spatial - hostname: api-data-mysql-spatial - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.MySql.Spatial - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.MySql.Spatial/.dockerignore b/Api.Data.MySql.Spatial/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.MySql.Spatial/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.MySql.Spatial/.github/config/slack.yml b/Api.Data.MySql.Spatial/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.MySql.Spatial/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml deleted file mode 100644 index c1658a02..00000000 --- a/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.MySql.Spatial - IMAGE_NAME: api.data.mysql.spatial - SERVICE_NAME: api-data-mysql-spatial - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/.gitignore b/Api.Data.MySql.Spatial/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.MySql.Spatial/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/.kubernetes/autoscaler.yaml b/Api.Data.MySql.Spatial/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.MySql.Spatial/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml b/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml b/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.Spatial/.kubernetes/service.yaml b/Api.Data.MySql.Spatial/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.MySql.Spatial/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Properties/DoNotParallelize.cs b/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Tests.Api.Data.MySql.Spatial.csproj b/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Tests.Api.Data.MySql.Spatial.csproj deleted file mode 100644 index 8ea090d1..00000000 --- a/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Tests.Api.Data.MySql.Spatial.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 198440ff..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,46 +0,0 @@ -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; -using NetTopologySuite.Geometries; -using System.Collections.Generic; -using System.Linq; - -namespace Api.Data.MySql.Spatial.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Point. - /// - public virtual Point? Point { get; set; } - - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (this.Point != null) - { - expression - .IsWithinDistance(nameof(Example.Point), this.Point, 10000); - } - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Example.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Example.cs deleted file mode 100644 index 2e16f95a..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Example.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; -using NetTopologySuite.Geometries; - -namespace Api.Data.MySql.Spatial.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Point. - /// - [Required] - public virtual Point Point { get; set; } = null!; - - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.sln b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.sln deleted file mode 100644 index 37592f84..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Spatial.Models", "Api.Data.MySql.Spatial.Models\Api.Data.MySql.Spatial.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Spatial", "Api.Data.MySql.Spatial\Api.Data.MySql.Spatial.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.Spatial", ".tests\Tests.Api.Data.MySql.Spatial\Tests.Api.Data.MySql.Spatial.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.csproj b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.csproj deleted file mode 100644 index cab843f5..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Controllers/ExamplesController.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Controllers/ExamplesController.cs deleted file mode 100644 index 55588adc..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Spatial.Models; -using Api.Data.MySql.Spatial.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Spatial.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 657d2a66..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Api.Data.MySql.Spatial.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; -using NetTopologySuite.Geometries; - -namespace Api.Data.MySql.Spatial.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Point) - .HasColumnType("POINT") - .HasSpatialReferenceSystem(4326) - .HasDefaultValue(new Point(0, 0) { SRID = 4326 }) - .IsRequired(); - - builder - .HasIndex(x => x.Point) - .IsSpatial(); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContext.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContext.cs deleted file mode 100644 index 577d6fc9..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.MySql.Spatial.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 153aed1c..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.MySql.Spatial.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local deleted file mode 100644 index 33203aa6..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.MySql.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.Designer.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.Designer.cs deleted file mode 100644 index 57095ab5..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.Designer.cs +++ /dev/null @@ -1,662 +0,0 @@ -// -using System; -using Api.Data.MySql.Spatial.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using NetTopologySuite.Geometries; - -#nullable disable - -namespace Api.Data.MySql.Spatial.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415143931_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Spatial.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("Point") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("POINT") - .HasDefaultValue((NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")) - .HasAnnotation("MySql:SpatialReferenceSystemId", 4326); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.HasIndex("Point") - .HasAnnotation("MySql:SpatialIndex", true); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.cs deleted file mode 100644 index b036340c..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.cs +++ /dev/null @@ -1,610 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using NetTopologySuite.Geometries; - -#nullable disable - -namespace Api.Data.MySql.Spatial.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Point = table.Column(type: "point", nullable: false, defaultValue: (NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")) - .Annotation("MySql:SpatialReferenceSystemId", 4326), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Point", - table: "Example", - column: "Point") - .Annotation("MySql:SpatialIndex", true); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 0ba27d24..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,659 +0,0 @@ -// -using System; -using Api.Data.MySql.Spatial.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using NetTopologySuite.Geometries; - -#nullable disable - -namespace Api.Data.MySql.Spatial.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Spatial.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("Point") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("POINT") - .HasDefaultValue((NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")) - .HasAnnotation("MySql:SpatialReferenceSystemId", 4326); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.HasIndex("Point") - .HasAnnotation("MySql:SpatialIndex", true); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Program.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Program.cs deleted file mode 100644 index cec8e9f2..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.MySql.Spatial.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Properties/InternalsVisibleTo.cs deleted file mode 100644 index d42997e9..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.Spatial")] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Production.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Staging.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.json deleted file mode 100644 index d43a066a..00000000 --- a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Dockerfile b/Api.Data.MySql.Spatial/Dockerfile deleted file mode 100644 index dce34833..00000000 --- a/Api.Data.MySql.Spatial/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.MySql.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/LICENSE b/Api.Data.MySql.Spatial/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.MySql.Spatial/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/README.md b/Api.Data.MySql.Spatial/README.md deleted file mode 100644 index 14b9a5ef..00000000 --- a/Api.Data.MySql.Spatial/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Api.Data.MySql.Spatial - -> _Nano API application with mysql spatial data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been simplified to -showcase spatial types; full controllers are unnecessary. - -The `Example` entity now includes a `Point` property from `NetTopologySuite`. A query criterion has been added to check whether points are within a 10,000 meter distance. The -entity mappings for this spatial property have also been configured. Otherwise, no other changes were made. - -> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql)**. diff --git a/Api.Data.MySql.Spatial/icon.png b/Api.Data.MySql.Spatial/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml b/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml deleted file mode 100644 index 5f01dfb6..00000000 --- a/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.mysql.storedprocedures: - image: api.data.mysql.storedprocedures - hostname: api-data-mysql-storedprocedures - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.MySql.StoredProcedures - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.MySql.StoredProcedures/.dockerignore b/Api.Data.MySql.StoredProcedures/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.MySql.StoredProcedures/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.MySql.StoredProcedures/.github/config/slack.yml b/Api.Data.MySql.StoredProcedures/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.MySql.StoredProcedures/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 7d9126d4..00000000 --- a/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.MySql.StoredProcedures - IMAGE_NAME: api.data.mysql.storedprocedures - SERVICE_NAME: api-data-mysql-storedprocedures - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/.gitignore b/Api.Data.MySql.StoredProcedures/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.MySql.StoredProcedures/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/autoscaler.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.MySql.StoredProcedures/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/service.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.MySql.StoredProcedures/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Properties/DoNotParallelize.cs b/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Tests.Api.Data.MySql.StoredProcedures.csproj b/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Tests.Api.Data.MySql.StoredProcedures.csproj deleted file mode 100644 index ff6e17e9..00000000 --- a/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Tests.Api.Data.MySql.StoredProcedures.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Example.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Example.cs deleted file mode 100644 index 7117dc91..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Example.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Nano.Data.Abstractions.Models; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; - -namespace Api.Data.MySql.StoredProcedures.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; - - /// - /// Name. - /// - [Required] - [DefaultValue(0)] - public virtual int Counter { get; set; } = 0; -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/ExampleResult.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/ExampleResult.cs deleted file mode 100644 index cb7156c8..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/ExampleResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Nano.Data.Abstractions.Models; -using Nano.Data.Abstractions.Models.Abstractions; - -namespace Api.Data.MySql.StoredProcedures.Models; - -/// -/// Example Result. -/// -public class ExampleResult : BaseEntityView -{ - /// - /// Id. - /// - public virtual Guid Id { get; set; } - - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; - - /// - /// Counter. - /// - public virtual int Counter { get; set; } = 0; -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.sln b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.sln deleted file mode 100644 index 49bb7bb8..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.StoredProcedures.Models", "Api.Data.MySql.StoredProcedures.Models\Api.Data.MySql.StoredProcedures.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.StoredProcedures", "Api.Data.MySql.StoredProcedures\Api.Data.MySql.StoredProcedures.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.StoredProcedures", ".tests\Tests.Api.Data.MySql.StoredProcedures\Tests.Api.Data.MySql.StoredProcedures.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.csproj b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.csproj deleted file mode 100644 index 9fcba059..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Controllers/ExamplesController.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Controllers/ExamplesController.cs deleted file mode 100644 index f4365bf2..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Controllers/ExamplesController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.Data.MySql.StoredProcedures.Data.Extensions; - -namespace Api.Data.MySql.StoredProcedures.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository) -{ - /// - /// Stored Procedure Action. - /// - /// The example name. - /// The cancellation token. - /// A result. - /// Success. - [HttpGet] - [Route("stored-procedure")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task StoredProcedureAsync([Required]string name, CancellationToken cancellationToken = default) - { - var result = await this.Repository - .GetExampleResult(name, cancellationToken); - - return this.Ok(result); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Extensions/RepositoryExtensions.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Extensions/RepositoryExtensions.cs deleted file mode 100644 index 00df47e9..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Extensions/RepositoryExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Api.Data.MySql.StoredProcedures.Data.StoredProcedures; -using Api.Data.MySql.StoredProcedures.Models; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.StoredProcedures.Data.Extensions; - -internal static class RepositoryExtensions -{ - internal static async Task GetExampleResult(this IRepository repository, string name, CancellationToken cancellationToken = default) - { - ArgumentNullException.ThrowIfNull(repository); - ArgumentNullException.ThrowIfNull(name); - - var dict = new Dictionary - { - { "p_name", name } - }; - - var result = await repository - .ExecuteProcedureAsync(ExampleStoredProcedureDefinition.ExampleCreateOrIncrement, dict, cancellationToken); - - return result; - } -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 10ef53ef..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Api.Data.MySql.StoredProcedures.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.StoredProcedures.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - - builder - .Property(x => x.Counter) - .HasDefaultValue(0) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContext.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContext.cs deleted file mode 100644 index 1a70ac2d..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.MySql.StoredProcedures.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 7cb2bf45..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.MySql.StoredProcedures.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/StoredProcedures/StoredProcedures.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/StoredProcedures/StoredProcedures.cs deleted file mode 100644 index aa454296..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/StoredProcedures/StoredProcedures.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Api.Data.MySql.StoredProcedures.Models; - -namespace Api.Data.MySql.StoredProcedures.Data.StoredProcedures; - -internal static class ExampleStoredProcedureDefinition -{ - internal static string ExampleCreateOrIncrement => nameof(ExampleStoredProcedureDefinition.ExampleCreateOrIncrement); - - internal const string SQL = $@" - DROP PROCEDURE IF EXISTS {nameof(ExampleStoredProcedureDefinition.ExampleCreateOrIncrement)}; - - CREATE PROCEDURE {nameof(ExampleStoredProcedureDefinition.ExampleCreateOrIncrement)} ( - IN p_name VARCHAR(255) - ) - BEGIN - DECLARE v_id CHAR(36); - SELECT {nameof(Example.Id)} INTO v_id FROM {nameof(Example)} WHERE {nameof(Example.Name)} = p_name LIMIT 1; - - IF v_id IS NOT NULL THEN - UPDATE {nameof(Example)} SET {nameof(Example.Counter)} = {nameof(Example.Counter)} + 1 WHERE {nameof(Example.Id)} = v_id; - ELSE - SET v_id = UUID(); - INSERT INTO {nameof(Example)} ({nameof(Example.Id)}, {nameof(Example.Name)}, {nameof(Example.Counter)}) VALUES (v_id, p_name, 1); - END IF; - - SELECT {nameof(Example.Id)}, {nameof(Example.Name)}, {nameof(Example.Counter)} FROM {nameof(Example)} WHERE {nameof(Example.Id)} = v_id; - END"; -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local deleted file mode 100644 index e3651ec7..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.MySql.StoredProcedures.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.Designer.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.Designer.cs deleted file mode 100644 index 53eebeac..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.Designer.cs +++ /dev/null @@ -1,656 +0,0 @@ -// -using System; -using Api.Data.MySql.StoredProcedures.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.StoredProcedures.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415151833_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.StoredProcedures.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Counter") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.cs deleted file mode 100644 index f22911dd..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.cs +++ /dev/null @@ -1,602 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.MySql.StoredProcedures.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Counter = table.Column(type: "int", nullable: false, defaultValue: 0), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.Designer.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.Designer.cs deleted file mode 100644 index 6f337ac2..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.Designer.cs +++ /dev/null @@ -1,656 +0,0 @@ -// -using System; -using Api.Data.MySql.StoredProcedures.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.StoredProcedures.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415151941_AddedStoredProcedure")] - partial class AddedStoredProcedure - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.StoredProcedures.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Counter") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.cs deleted file mode 100644 index a57640c5..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Api.Data.MySql.StoredProcedures.Data.StoredProcedures; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.MySql.StoredProcedures.Migrations -{ - /// - public partial class AddedStoredProcedure : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder - .Sql(ExampleStoredProcedureDefinition.SQL); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 6f75602c..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,653 +0,0 @@ -// -using System; -using Api.Data.MySql.StoredProcedures.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.StoredProcedures.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.StoredProcedures.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("Counter") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Program.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Program.cs deleted file mode 100644 index 03d52d13..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.MySql.StoredProcedures.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 10667533..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.StoredProcedures")] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Production.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Staging.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.json deleted file mode 100644 index d43a066a..00000000 --- a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Dockerfile b/Api.Data.MySql.StoredProcedures/Dockerfile deleted file mode 100644 index 81df7ff6..00000000 --- a/Api.Data.MySql.StoredProcedures/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.MySql.StoredProcedures.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/LICENSE b/Api.Data.MySql.StoredProcedures/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.MySql.StoredProcedures/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/README.md b/Api.Data.MySql.StoredProcedures/README.md deleted file mode 100644 index a0c7850a..00000000 --- a/Api.Data.MySql.StoredProcedures/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Api.Data.MySql.StoredProcedures - -> _Nano API application with mysql stored procedure data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been -simplified to showcase mysql stored procedures; full controllers are unnecessary. - -This example defines a stored procedure, and creates it during a migration. - -```csharp -migrationBuilder - .Sql(ExampleStoredProcedureDefinition.SQL); -``` - -The stored procedure is executed using Nano’s `IRepository.ExecuteAsync(...)` via this application's extension method `GetExampleResult`. The method simply wraps the -repository call to provide a clear, strongly typed invocation point for use by the controller. - -The following endpoint is available for testing: - -| Endpoint | Description | -| ------------------------------------------------------ | ------------------------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/stored-procedure` | Returns a simple `200 OK` response with the result of the stored procedure as response. | - -> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql)**. diff --git a/Api.Data.MySql.StoredProcedures/icon.png b/Api.Data.MySql.StoredProcedures/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Views/.docker/docker-compose.yml b/Api.Data.MySql.Views/.docker/docker-compose.yml deleted file mode 100644 index 8a5c7578..00000000 --- a/Api.Data.MySql.Views/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.mysql.views: - image: api.data.mysql.views - hostname: api-data-mysql-views - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.MySql.Views - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.MySql.Views/.dockerignore b/Api.Data.MySql.Views/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.MySql.Views/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.MySql.Views/.github/config/slack.yml b/Api.Data.MySql.Views/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.MySql.Views/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 84724148..00000000 --- a/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.MySql.Views - IMAGE_NAME: api.data.mysql.views - SERVICE_NAME: api-data-mysql-views - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.Views/.gitignore b/Api.Data.MySql.Views/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.MySql.Views/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.Views/.kubernetes/autoscaler.yaml b/Api.Data.MySql.Views/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.MySql.Views/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.Views/.kubernetes/configmap.yaml b/Api.Data.MySql.Views/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.MySql.Views/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.Views/.kubernetes/deployment.yaml b/Api.Data.MySql.Views/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.MySql.Views/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql.Views/.kubernetes/service.yaml b/Api.Data.MySql.Views/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.MySql.Views/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Properties/DoNotParallelize.cs b/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Tests.Api.Data.MySql.Views.csproj b/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Tests.Api.Data.MySql.Views.csproj deleted file mode 100644 index 8d17099b..00000000 --- a/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Tests.Api.Data.MySql.Views.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 32eca2ec..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.MySql.Views.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Example.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Example.cs deleted file mode 100644 index e991ad4b..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Views.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/ExampleView.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/ExampleView.cs deleted file mode 100644 index d0fcb181..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/ExampleView.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Views.Models; - -/// -/// Example View. -/// -public class ExampleView : BaseEntityView -{ - /// - /// Id. - /// - public virtual Guid Id { get; set; } - - /// - /// Created At. - /// - public virtual DateTimeOffset CreatedAt { get; set; } - - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; - - /// - /// Name Length. - /// - public virtual int NameLength { get; set; } -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.sln b/Api.Data.MySql.Views/Api.Data.MySql.Views.sln deleted file mode 100644 index 1233c8be..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Views.Models", "Api.Data.MySql.Views.Models\Api.Data.MySql.Views.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Views", "Api.Data.MySql.Views\Api.Data.MySql.Views.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.Views", ".tests\Tests.Api.Data.MySql.Views\Tests.Api.Data.MySql.Views.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Api.Data.MySql.Views.csproj b/Api.Data.MySql.Views/Api.Data.MySql.Views/Api.Data.MySql.Views.csproj deleted file mode 100644 index c440c4e6..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Api.Data.MySql.Views.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExampleViewsController.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExampleViewsController.cs deleted file mode 100644 index 89264bf1..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExampleViewsController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Views.Models; -using Api.Data.MySql.Views.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Views.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleViewsController(ILogger logger, IRepository repository) - : BaseEntityViewController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExamplesController.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExamplesController.cs deleted file mode 100644 index 32f773ee..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Views.Models; -using Api.Data.MySql.Views.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Views.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 89e6a6e7..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using Api.Data.MySql.Views.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Views.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleViewMapping.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleViewMapping.cs deleted file mode 100644 index fade453c..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleViewMapping.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Api.Data.MySql.Views.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Views.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleViewMapping : BaseEntityViewMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Id); - - builder - .Property(x => x.CreatedAt); - - builder - .Property(x => x.Name); - - builder - .Property(x => x.NameLength); - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContext.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContext.cs deleted file mode 100644 index 303dcd55..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.MySql.Views.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 8c365ec7..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.MySql.Views.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Views/ExampleViewDefinition.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Views/ExampleViewDefinition.cs deleted file mode 100644 index f0ac49d2..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Views/ExampleViewDefinition.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Views.Models; - -namespace Api.Data.MySql.Views.Data.Views; - -internal static class ExampleViewDefinition -{ - internal const string SQL = $@" - CREATE OR REPLACE VIEW {nameof(ExampleView)} AS - SELECT - {nameof(Example.Id)}, - {nameof(Example.CreatedAt)}, - {nameof(Example.Name)}, - CHAR_LENGTH({nameof(Example.Name)}) AS {nameof(ExampleView.NameLength)} - FROM {nameof(Example)};"; -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local b/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local deleted file mode 100644 index bac8ec3f..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.MySql.Views.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.Designer.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.Designer.cs deleted file mode 100644 index f9ab6d19..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.Designer.cs +++ /dev/null @@ -1,669 +0,0 @@ -// -using System; -using Api.Data.MySql.Views.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Views.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415152009_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Views.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.MySql.Views.Models.ExampleView", b => - { - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("NameLength") - .HasColumnType("int"); - - b.ToTable((string)null); - - b.ToView("ExampleView", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.cs deleted file mode 100644 index ad56c19b..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.cs +++ /dev/null @@ -1,596 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.MySql.Views.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.Designer.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.Designer.cs deleted file mode 100644 index eda72e69..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.Designer.cs +++ /dev/null @@ -1,669 +0,0 @@ -// -using System; -using Api.Data.MySql.Views.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Views.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415152149_AddedView")] - partial class AddedView - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Views.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.MySql.Views.Models.ExampleView", b => - { - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("NameLength") - .HasColumnType("int"); - - b.ToTable((string)null); - - b.ToView("ExampleView", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.cs deleted file mode 100644 index 70b7d706..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Api.Data.MySql.Views.Data.Views; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.MySql.Views.Migrations -{ - /// - public partial class AddedView : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder - .Sql(ExampleViewDefinition.SQL); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 9b0bbc85..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,666 +0,0 @@ -// -using System; -using Api.Data.MySql.Views.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Views.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Views.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.MySql.Views.Models.ExampleView", b => - { - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Id") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("NameLength") - .HasColumnType("int"); - - b.ToTable((string)null); - - b.ToView("ExampleView", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Program.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Program.cs deleted file mode 100644 index 74ae1422..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.MySql.Views.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 4a7c6b5e..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.Views")] \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Production.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Staging.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.json deleted file mode 100644 index d43a066a..00000000 --- a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Dockerfile b/Api.Data.MySql.Views/Dockerfile deleted file mode 100644 index 9e00750f..00000000 --- a/Api.Data.MySql.Views/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.MySql.Views.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Views/LICENSE b/Api.Data.MySql.Views/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.MySql.Views/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.Views/README.md b/Api.Data.MySql.Views/README.md deleted file mode 100644 index 73f0e7a6..00000000 --- a/Api.Data.MySql.Views/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Api.Data.MySql.Views - -> _Nano API application with mysql views data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been -simplified to showcase mysql views; full controllers are unnecessary. - -An `ExampleView` entity model (deriving from `BaseEntityView`) has been added, along with a mapping class based on `BaseEntityViewMapping`. The corresponding database view has -been manually added in an empty migration. - -```csharp -migrationBuilder - .Sql(ExampleViewDefinition.SQL); -``` - -Also, an `ExampleViewsController` (deriving from `BaseEntityViewController`) has been added, exposing query actions for the view. - -> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql)**. diff --git a/Api.Data.MySql.Views/icon.png b/Api.Data.MySql.Views/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.MySql/.docker/docker-compose.yml b/Api.Data.MySql/.docker/docker-compose.yml deleted file mode 100644 index e1757ea8..00000000 --- a/Api.Data.MySql/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.mysql: - image: api.data.mysql - hostname: api-data-mysql - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.MySql - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.MySql/.dockerignore b/Api.Data.MySql/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.MySql/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.MySql/.github/config/slack.yml b/Api.Data.MySql/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.MySql/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql/.github/workflows/build-and-deploy.yml b/Api.Data.MySql/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 7731f00f..00000000 --- a/Api.Data.MySql/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.MySql - IMAGE_NAME: api.data.mysql - SERVICE_NAME: api-data-mysql - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql/.gitignore b/Api.Data.MySql/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.MySql/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql/.kubernetes/autoscaler.yaml b/Api.Data.MySql/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.MySql/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql/.kubernetes/configmap.yaml b/Api.Data.MySql/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.MySql/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql/.kubernetes/deployment.yaml b/Api.Data.MySql/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.MySql/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.MySql/.kubernetes/service.yaml b/Api.Data.MySql/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.MySql/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Properties/DoNotParallelize.cs b/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Tests.Api.Data.MySql.csproj b/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Tests.Api.Data.MySql.csproj deleted file mode 100644 index af99d724..00000000 --- a/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Tests.Api.Data.MySql.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj b/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj deleted file mode 100644 index 1a34a185..00000000 --- a/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - NU1902; NU1903; NU5104 - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql/Api.Data.MySql.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index d239fafa..00000000 --- a/Api.Data.MySql/Api.Data.MySql.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.MySql.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/Example.cs b/Api.Data.MySql/Api.Data.MySql.Models/Example.cs deleted file mode 100644 index 581b42c9..00000000 --- a/Api.Data.MySql/Api.Data.MySql.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatable.cs b/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatable.cs deleted file mode 100644 index cc3b1b10..00000000 --- a/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Models; - -/// -/// Example. -/// -public class ExampleCreatable : BaseEntityCreatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatableAndEditable.cs b/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatableAndEditable.cs deleted file mode 100644 index ef663527..00000000 --- a/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatableAndEditable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Models; - -/// -/// Example. -/// -public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/ExampleDeletable.cs b/Api.Data.MySql/Api.Data.MySql.Models/ExampleDeletable.cs deleted file mode 100644 index 2e82d8c0..00000000 --- a/Api.Data.MySql/Api.Data.MySql.Models/ExampleDeletable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Models; - -/// -/// Example. -/// -public class ExampleDeletable : BaseEntityDeletable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/ExampleUpdatable.cs b/Api.Data.MySql/Api.Data.MySql.Models/ExampleUpdatable.cs deleted file mode 100644 index 9be420fa..00000000 --- a/Api.Data.MySql/Api.Data.MySql.Models/ExampleUpdatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.MySql.Models; - -/// -/// Example. -/// -public class ExampleUpdatable : BaseEntityUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.sln b/Api.Data.MySql/Api.Data.MySql.sln deleted file mode 100644 index 60556ddd..00000000 --- a/Api.Data.MySql/Api.Data.MySql.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Models", "Api.Data.MySql.Models\Api.Data.MySql.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql", "Api.Data.MySql\Api.Data.MySql.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql", ".tests\Tests.Api.Data.MySql\Tests.Api.Data.MySql.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.MySql/Api.Data.MySql/Api.Data.MySql.csproj b/Api.Data.MySql/Api.Data.MySql/Api.Data.MySql.csproj deleted file mode 100644 index b6f023d3..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Api.Data.MySql.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreatableAndEditableController.cs deleted file mode 100644 index 1dc556cc..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreatableAndEditableController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Models; -using Api.Data.MySql.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) - : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreateablesController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreateablesController.cs deleted file mode 100644 index 279a0d52..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreateablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Models; -using Api.Data.MySql.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreateablesController(ILogger logger, IRepository repository) - : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleDeletablesController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleDeletablesController.cs deleted file mode 100644 index 8abbb5e2..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleDeletablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Models; -using Api.Data.MySql.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleDeletablesController(ILogger logger, IRepository repository) - : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleUpdatablesController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleUpdatablesController.cs deleted file mode 100644 index 26193a42..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleUpdatablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.MySql.Models; -using Api.Data.MySql.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleUpdatablesController(ILogger logger, IRepository repository) - : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExamplesController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExamplesController.cs deleted file mode 100644 index 89ce8a36..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Api.Data.MySql.Models; -using Api.Data.MySql.Models.Criterias; -using Nano.Data.Abstractions; - -namespace Api.Data.MySql.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs deleted file mode 100644 index 59b3f9fd..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.MySql.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Data.Mappings; - -/// -/// Example Creatable And Updatable Mapping. -/// -public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableMapping.cs deleted file mode 100644 index 563bf221..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.MySql.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Data.Mappings; - -/// -/// Example Creatable Mapping. -/// -public class ExampleCreatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleDeletableMapping.cs deleted file mode 100644 index 08bbde35..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleDeletableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.MySql.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Data.Mappings; - -/// -/// Example Deletable Mapping. -/// -public class ExampleDeletableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index d33c25ae..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using Api.Data.MySql.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name) - .IsUnique(); - } -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleUpdatableMapping.cs deleted file mode 100644 index ecfb3efb..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.MySql.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.MySql.Data.Mappings; - -/// -/// Example Updatable Mapping. -/// -public class ExampleUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContext.cs b/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContext.cs deleted file mode 100644 index aebe8955..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.MySql.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContextFactory.cs b/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 19978f90..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.MySql.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local b/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local deleted file mode 100644 index 2c0d1470..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.MySql.dll"] \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.Designer.cs b/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.Designer.cs deleted file mode 100644 index 088b3369..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.Designer.cs +++ /dev/null @@ -1,781 +0,0 @@ -// -using System; -using Api.Data.MySql.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260423071339_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name") - .IsUnique() - .HasDatabaseName("UX_Example_Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.MySql.Models.ExampleCreatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatable"); - }); - - modelBuilder.Entity("Api.Data.MySql.Models.ExampleCreatableAndEditable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatableAndEditable"); - }); - - modelBuilder.Entity("Api.Data.MySql.Models.ExampleDeletable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleDeletable"); - }); - - modelBuilder.Entity("Api.Data.MySql.Models.ExampleUpdatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleUpdatable"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.cs b/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.cs deleted file mode 100644 index 54072bfd..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.cs +++ /dev/null @@ -1,742 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.MySql.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleCreatable", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleCreatable", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleCreatableAndEditable", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleCreatableAndEditable", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleDeletable", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleDeletable", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleUpdatable", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleUpdatable", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "UX_Example_Name", - table: "Example", - column: "Name", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_CreatedAt", - table: "ExampleCreatable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_IsDeleted", - table: "ExampleCreatable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_Name", - table: "ExampleCreatable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_CreatedAt", - table: "ExampleCreatableAndEditable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_IsDeleted", - table: "ExampleCreatableAndEditable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_Name", - table: "ExampleCreatableAndEditable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_CreatedAt", - table: "ExampleDeletable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_IsDeleted", - table: "ExampleDeletable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_Name", - table: "ExampleDeletable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_CreatedAt", - table: "ExampleUpdatable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_IsDeleted", - table: "ExampleUpdatable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_Name", - table: "ExampleUpdatable", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "ExampleCreatable"); - - migrationBuilder.DropTable( - name: "ExampleCreatableAndEditable"); - - migrationBuilder.DropTable( - name: "ExampleDeletable"); - - migrationBuilder.DropTable( - name: "ExampleUpdatable"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.MySql/Api.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql/Api.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 968c51dc..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,778 +0,0 @@ -// -using System; -using Api.Data.MySql.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.MySql.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.MySql.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name") - .IsUnique() - .HasDatabaseName("UX_Example_Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.MySql.Models.ExampleCreatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatable"); - }); - - modelBuilder.Entity("Api.Data.MySql.Models.ExampleCreatableAndEditable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatableAndEditable"); - }); - - modelBuilder.Entity("Api.Data.MySql.Models.ExampleDeletable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleDeletable"); - }); - - modelBuilder.Entity("Api.Data.MySql.Models.ExampleUpdatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleUpdatable"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.MySql/Api.Data.MySql/Program.cs b/Api.Data.MySql/Api.Data.MySql/Program.cs deleted file mode 100644 index c8b8763b..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.MySql.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.MySql/Api.Data.MySql/Properties/InternalsVisibleTo.cs b/Api.Data.MySql/Api.Data.MySql/Properties/InternalsVisibleTo.cs deleted file mode 100644 index bfdea35b..00000000 --- a/Api.Data.MySql/Api.Data.MySql/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.MySql")] \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json b/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.Production.json b/Api.Data.MySql/Api.Data.MySql/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql/Api.Data.MySql/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.Staging.json b/Api.Data.MySql/Api.Data.MySql/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.MySql/Api.Data.MySql/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.json b/Api.Data.MySql/Api.Data.MySql/appsettings.json deleted file mode 100644 index 2871c661..00000000 --- a/Api.Data.MySql/Api.Data.MySql/appsettings.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - }, - "Documentation": { - "Name": "Application" - } - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "StartupAction": "None", - "UseLazyLoading": false, - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": true, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } -} \ No newline at end of file diff --git a/Api.Data.MySql/Dockerfile b/Api.Data.MySql/Dockerfile deleted file mode 100644 index d88ccf17..00000000 --- a/Api.Data.MySql/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.MySql.dll"] \ No newline at end of file diff --git a/Api.Data.MySql/LICENSE b/Api.Data.MySql/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.MySql/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql/README.md b/Api.Data.MySql/README.md deleted file mode 100644 index 2459fc1d..00000000 --- a/Api.Data.MySql/README.md +++ /dev/null @@ -1,196 +0,0 @@ -# Api.Data.MySql - -> _Nano API application with mysql data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from -the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings)**, -and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context)**. - -Additionally, the example shows how Nano **[Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories)** works along with the corresponding -entity controllers. For more information on controllers and how they are connected with entity models, see **[Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#controllers)**. - -A data health check is configured to target the database. -Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#health-checks)**. - -Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed -here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing -the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. - -> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -Also, an initial migration has been added to the project. - -```powershell -dotnet ef migrations add Initial --project Api.Data.MySql -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "StartupAction": "None", - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } -} -``` - -...and `appsettings.Development.json`. - -```json -"Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" -} -``` - -## Docker Compose -Added MySql as a service dependency in `docker-compose.yml`. - -```yaml -services: - api.data.mysql: - depends_on: - - database - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' -``` - -## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. - -```json -spec: - template: - spec: - containers: - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -``` - -Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING; - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } -``` - -Last, the application connectionstring must be added in a secret in Kuberntes. The `Kubernetes Deploy` step has been updated with the following. - -```yaml -sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Api.Data.MySql/icon.png b/Api.Data.MySql/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/.docker/docker-compose.yml b/Api.Data.PostgreSQL.Spatial/.docker/docker-compose.yml deleted file mode 100644 index 80392323..00000000 --- a/Api.Data.PostgreSQL.Spatial/.docker/docker-compose.yml +++ /dev/null @@ -1,30 +0,0 @@ -services: - api.data.postgresql.spatial: - image: api.data.postgresql.spatial - hostname: api-data-postgresql-spatial - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.PostgreSQL.Spatial - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: postgis/postgis:latest - ports: - - 5432:5432 - networks: - - network - environment: - POSTGRES_USER: sa - POSTGRES_PASSWORD: myPassword_123 - POSTGRES_DB: nanoDb - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.PostgreSQL.Spatial/.dockerignore b/Api.Data.PostgreSQL.Spatial/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.PostgreSQL.Spatial/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.PostgreSQL.Spatial/.github/config/slack.yml b/Api.Data.PostgreSQL.Spatial/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.PostgreSQL.Spatial/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml deleted file mode 100644 index f4e23d90..00000000 --- a/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,228 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.PostgreSQL.Spatial - IMAGE_NAME: api.data.postgresql.spatial - SERVICE_NAME: api-data-postgresql-spatial - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_HOST || secrets.STAGING_POSTGRE_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-postgre-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_NANO_DB_PASSWORD || secrets.STAGING_POSTGRE_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_USER || secrets.STAGING_POSTGRE_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_PASSWORD || secrets.STAGING_POSTGRE_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true - DATA_MIGRATION_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y postgresql-client - - $userExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -c "CREATE ROLE $env:DATA_USER WITH LOGIN PASSWORD '$env:DATA_PASSWORD';" - } - - $userDbExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userDbExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT CONNECT ON DATABASE $env:DATA_NAME TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT USAGE ON SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:DATA_USER;" - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/.gitignore b/Api.Data.PostgreSQL.Spatial/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.PostgreSQL.Spatial/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/autoscaler.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.PostgreSQL.Spatial/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/service.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.PostgreSQL.Spatial/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Properties/DoNotParallelize.cs b/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Tests.Api.Data.PostgreSQL.Spatial.csproj b/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Tests.Api.Data.PostgreSQL.Spatial.csproj deleted file mode 100644 index b54bb791..00000000 --- a/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Tests.Api.Data.PostgreSQL.Spatial.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj deleted file mode 100644 index 9b2d874d..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 9a8820f3..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; -using NetTopologySuite.Geometries; - -namespace Api.Data.PostgreSQL.Spatial.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Point. - /// - public virtual Point? Point { get; set; } - - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (this.Point != null) - { - expression - .IsWithinDistance(nameof(Example.Point), this.Point, 10000); - } - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Example.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Example.cs deleted file mode 100644 index 667f50ad..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Example.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; -using NetTopologySuite.Geometries; - -namespace Api.Data.PostgreSQL.Spatial.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Point. - /// - [Required] - public virtual Point Point { get; set; } = null!; - - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.sln b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.sln deleted file mode 100644 index 28698abc..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.PostgreSQL.Spatial.Models", "Api.Data.PostgreSQL.Spatial.Models\Api.Data.PostgreSQL.Spatial.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.PostgreSQL.Spatial", "Api.Data.PostgreSQL.Spatial\Api.Data.PostgreSQL.Spatial.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.PostgreSQL.Spatial", ".tests\Tests.Api.Data.PostgreSQL.Spatial\Tests.Api.Data.PostgreSQL.Spatial.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.PostgreSQL", "..\..\Nano.Library\Nano.Data.PostgreSQL\Nano.Data.PostgreSQL.csproj", "{B898497D-7C77-3353-B323-399EBAF53DAC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B898497D-7C77-3353-B323-399EBAF53DAC} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.csproj b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.csproj deleted file mode 100644 index 3ee2a917..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Controllers/ExamplesController.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Controllers/ExamplesController.cs deleted file mode 100644 index 12dea508..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.PostgreSQL.Spatial.Models; -using Api.Data.PostgreSQL.Spatial.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.PostgreSQL.Spatial.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/Mappings/ExampleMapping.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index a252eeed..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using Api.Data.PostgreSQL.Spatial.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; -using NetTopologySuite.Geometries; - -namespace Api.Data.PostgreSQL.Spatial.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Point) - .HasColumnType("geography(Point,4326)") - .HasDefaultValue(new Point(0, 0) { SRID = 4326 }) - .IsRequired(); - - builder - .HasIndex(x => x.Point) - .HasMethod("GIST"); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContext.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContext.cs deleted file mode 100644 index a4a89e73..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.PostgreSQL.Spatial.Data; - -/// -public class PostgreSqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContextFactory.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContextFactory.cs deleted file mode 100644 index b67d1e78..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.PostgreSQL; - -namespace Api.Data.PostgreSQL.Spatial.Data; - -/// -public class PostgreSqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local deleted file mode 100644 index 9e8b385b..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.Designer.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.Designer.cs deleted file mode 100644 index 860d3470..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.Designer.cs +++ /dev/null @@ -1,661 +0,0 @@ -// -using System; -using Api.Data.PostgreSQL.Spatial.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using NetTopologySuite.Geometries; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Api.Data.PostgreSQL.Spatial.Migrations -{ - [DbContext(typeof(PostgreSqlDbContext))] - [Migration("20260415150018_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.PostgreSQL.Spatial.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("Point") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("geography(Point,4326)") - .HasDefaultValue((NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.HasIndex("Point"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Point"), "GIST"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("text"); - - b.Property("Xml") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityKey") - .HasColumnType("uuid"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("text"); - - b.Property("OldValue") - .HasColumnType("text"); - - b.Property("ParentId") - .HasColumnType("uuid"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("text"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ApiKeyId") - .HasColumnType("uuid"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ApiKeyId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.cs deleted file mode 100644 index a11df532..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.cs +++ /dev/null @@ -1,552 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using NetTopologySuite.Geometries; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Api.Data.PostgreSQL.Spatial.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:PostgresExtension:postgis", ",,"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - CreatedBy = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - EntityKey = table.Column(type: "uuid", nullable: false), - EntitySetName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EntityTypeName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - EntityState = table.Column(type: "integer", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - FriendlyName = table.Column(type: "text", nullable: true), - Xml = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IsActive = table.Column(type: "boolean", nullable: false, defaultValue: true), - UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "boolean", nullable: false), - PasswordHash = table.Column(type: "text", nullable: true), - SecurityStamp = table.Column(type: "text", nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true), - PhoneNumber = table.Column(type: "text", nullable: true), - PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), - TwoFactorEnabled = table.Column(type: "boolean", nullable: false), - LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), - LockoutEnabled = table.Column(type: "boolean", nullable: false), - AccessFailedCount = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Point = table.Column(type: "geography(Point,4326)", nullable: false, defaultValue: (NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")), - Name = table.Column(type: "text", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ParentId = table.Column(type: "uuid", nullable: false), - PropertyName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - RelationName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NewValue = table.Column(type: "text", nullable: true), - OldValue = table.Column(type: "text", nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - RoleId = table.Column(type: "uuid", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - Hash = table.Column(type: "text", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - RevokedAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - NewEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NewPhoneNumber = table.Column(type: "character varying(20)", maxLength: 20, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - UserId = table.Column(type: "uuid", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "text", nullable: false), - ProviderKey = table.Column(type: "text", nullable: false), - ProviderDisplayName = table.Column(type: "text", nullable: true), - UserId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - AppId = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - Value = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - ExpireAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "uuid", nullable: false), - RoleId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "uuid", nullable: false), - LoginProvider = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ApiKeyId = table.Column(type: "uuid", nullable: false), - ClaimType = table.Column(type: "text", nullable: false), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ApiKeyId = table.Column(type: "uuid", nullable: false), - RoleId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Point", - table: "Example", - column: "Point") - .Annotation("Npgsql:IndexMethod", "GIST"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/PostgreSqlDbContextModelSnapshot.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/PostgreSqlDbContextModelSnapshot.cs deleted file mode 100644 index 9488cea7..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/PostgreSqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,658 +0,0 @@ -// -using System; -using Api.Data.PostgreSQL.Spatial.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using NetTopologySuite.Geometries; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Api.Data.PostgreSQL.Spatial.Migrations -{ - [DbContext(typeof(PostgreSqlDbContext))] - partial class PostgreSqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.PostgreSQL.Spatial.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("Point") - .IsRequired() - .ValueGeneratedOnAdd() - .HasColumnType("geography(Point,4326)") - .HasDefaultValue((NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.HasIndex("Point"); - - NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Point"), "GIST"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("text"); - - b.Property("Xml") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityKey") - .HasColumnType("uuid"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("text"); - - b.Property("OldValue") - .HasColumnType("text"); - - b.Property("ParentId") - .HasColumnType("uuid"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("text"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ApiKeyId") - .HasColumnType("uuid"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ApiKeyId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs deleted file mode 100644 index 411bcfdc..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.PostgreSQL.Spatial.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.PostgreSQL; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Properties/InternalsVisibleTo.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Properties/InternalsVisibleTo.cs deleted file mode 100644 index e7caf604..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.PostgreSQL.Spatial")] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Development.json b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Development.json deleted file mode 100644 index ddba6f26..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Production.json b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Staging.json b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.json b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.json deleted file mode 100644 index d43a066a..00000000 --- a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Dockerfile b/Api.Data.PostgreSQL.Spatial/Dockerfile deleted file mode 100644 index a1c9d769..00000000 --- a/Api.Data.PostgreSQL.Spatial/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/LICENSE b/Api.Data.PostgreSQL.Spatial/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.PostgreSQL.Spatial/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/README.md b/Api.Data.PostgreSQL.Spatial/README.md deleted file mode 100644 index 70e5fd81..00000000 --- a/Api.Data.PostgreSQL.Spatial/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Api.Data.PostgreSQL.Spatial - -> _Nano API application with postgresql spatial data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.PostgreSQL)**. Entity controllers have been simplified to -showcase spatial types; full controllers are unnecessary. - -The `Example` entity now includes a `Point` property from `NetTopologySuite`. A query criterion has been added to check whether points are within a 10,000 meter distance. The -entity mappings for this spatial property have also been configured. Otherwise, no other changes were made. - -> 📖 Learn more about **[Nano.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.PostgreSQL)**. diff --git a/Api.Data.PostgreSQL.Spatial/icon.png b/Api.Data.PostgreSQL.Spatial/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.PostgreSQL/.docker/docker-compose.yml b/Api.Data.PostgreSQL/.docker/docker-compose.yml deleted file mode 100644 index 175643d3..00000000 --- a/Api.Data.PostgreSQL/.docker/docker-compose.yml +++ /dev/null @@ -1,30 +0,0 @@ -services: - api.data.postgresql: - image: api.data.postgresql - hostname: api-data-postgresql - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.PostgreSQL - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: postgis/postgis:latest - ports: - - 5432:5432 - networks: - - network - environment: - POSTGRES_USER: sa - POSTGRES_PASSWORD: myPassword_123 - POSTGRES_DB: nanoDb - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.PostgreSQL/.dockerignore b/Api.Data.PostgreSQL/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.PostgreSQL/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.PostgreSQL/.github/config/slack.yml b/Api.Data.PostgreSQL/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.PostgreSQL/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml b/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml deleted file mode 100644 index b47da275..00000000 --- a/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,228 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.PostgreSQL - IMAGE_NAME: api.data.postgresql - SERVICE_NAME: api-data-postgresql - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_HOST || secrets.STAGING_POSTGRE_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-postgre-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_NANO_DB_PASSWORD || secrets.STAGING_POSTGRE_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_USER || secrets.STAGING_POSTGRE_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_PASSWORD || secrets.STAGING_POSTGRE_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true - DATA_MIGRATION_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y postgresql-client - - $userExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -c "CREATE ROLE $env:DATA_USER WITH LOGIN PASSWORD '$env:DATA_PASSWORD';" - } - - $userDbExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userDbExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT CONNECT ON DATABASE $env:DATA_NAME TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT USAGE ON SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:DATA_USER;" - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/.gitignore b/Api.Data.PostgreSQL/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.PostgreSQL/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.PostgreSQL/.kubernetes/autoscaler.yaml b/Api.Data.PostgreSQL/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.PostgreSQL/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.PostgreSQL/.kubernetes/configmap.yaml b/Api.Data.PostgreSQL/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.PostgreSQL/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.PostgreSQL/.kubernetes/deployment.yaml b/Api.Data.PostgreSQL/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.PostgreSQL/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.PostgreSQL/.kubernetes/service.yaml b/Api.Data.PostgreSQL/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.PostgreSQL/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Properties/DoNotParallelize.cs b/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Tests.Api.Data.PostgreSQL.csproj b/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Tests.Api.Data.PostgreSQL.csproj deleted file mode 100644 index ad37ca78..00000000 --- a/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Tests.Api.Data.PostgreSQL.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj deleted file mode 100644 index 9b2d874d..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index c7b5cc8a..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.PostgreSQL.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Example.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Example.cs deleted file mode 100644 index e1e93b9f..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.PostgreSQL.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatable.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatable.cs deleted file mode 100644 index d9fb5d23..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.PostgreSQL.Models; - -/// -/// Example. -/// -public class ExampleCreatable : BaseEntityCreatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatableAndEditable.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatableAndEditable.cs deleted file mode 100644 index c2518608..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatableAndEditable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.PostgreSQL.Models; - -/// -/// Example. -/// -public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleDeletable.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleDeletable.cs deleted file mode 100644 index 8f2eca9b..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleDeletable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.PostgreSQL.Models; - -/// -/// Example. -/// -public class ExampleDeletable : BaseEntityDeletable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleUpdatable.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleUpdatable.cs deleted file mode 100644 index de5612f3..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleUpdatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.PostgreSQL.Models; - -/// -/// Example. -/// -public class ExampleUpdatable : BaseEntityUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln deleted file mode 100644 index 2aec4ee1..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.PostgreSQL.Models", "Api.Data.PostgreSQL.Models\Api.Data.PostgreSQL.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.PostgreSQL", "Api.Data.PostgreSQL\Api.Data.PostgreSQL.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.PostgreSQL", ".tests\Tests.Api.Data.PostgreSQL\Tests.Api.Data.PostgreSQL.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.PostgreSQL", "..\..\Nano.Library\Nano.Data.PostgreSQL\Nano.Data.PostgreSQL.csproj", "{B898497D-7C77-3353-B323-399EBAF53DAC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B898497D-7C77-3353-B323-399EBAF53DAC} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Api.Data.PostgreSQL.csproj b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Api.Data.PostgreSQL.csproj deleted file mode 100644 index e3c713b8..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Api.Data.PostgreSQL.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreatableAndEditableController.cs deleted file mode 100644 index 99346c5d..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreatableAndEditableController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.PostgreSQL.Models; -using Api.Data.PostgreSQL.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.PostgreSQL.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) - : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreateablesController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreateablesController.cs deleted file mode 100644 index f344a80f..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreateablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.PostgreSQL.Models; -using Api.Data.PostgreSQL.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.PostgreSQL.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreateablesController(ILogger logger, IRepository repository) - : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleDeletablesController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleDeletablesController.cs deleted file mode 100644 index 79cf46be..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleDeletablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.PostgreSQL.Models; -using Api.Data.PostgreSQL.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.PostgreSQL.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleDeletablesController(ILogger logger, IRepository repository) - : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleUpdatablesController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleUpdatablesController.cs deleted file mode 100644 index aedc1733..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleUpdatablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.PostgreSQL.Models; -using Api.Data.PostgreSQL.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.PostgreSQL.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleUpdatablesController(ILogger logger, IRepository repository) - : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExamplesController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExamplesController.cs deleted file mode 100644 index 8045bbad..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.PostgreSQL.Models; -using Api.Data.PostgreSQL.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.PostgreSQL.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs deleted file mode 100644 index 466b2e12..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.PostgreSQL.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.PostgreSQL.Data.Mappings; - -/// -/// Example Creatable And Updatable Mapping. -/// -public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableMapping.cs deleted file mode 100644 index 68904cfd..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.PostgreSQL.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.PostgreSQL.Data.Mappings; - -/// -/// Example Creatable Mapping. -/// -public class ExampleCreatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleDeletableMapping.cs deleted file mode 100644 index a7b1ba84..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleDeletableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.PostgreSQL.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.PostgreSQL.Data.Mappings; - -/// -/// Example Deletable Mapping. -/// -public class ExampleDeletableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index b8e39f21..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.PostgreSQL.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.PostgreSQL.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleUpdatableMapping.cs deleted file mode 100644 index b65030c2..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.PostgreSQL.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.PostgreSQL.Data.Mappings; - -/// -/// Example Updatable Mapping. -/// -public class ExampleUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContext.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContext.cs deleted file mode 100644 index 6f914d89..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.PostgreSQL.Data; - -/// -public class PostgreSqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs deleted file mode 100644 index 4ef0e410..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.PostgreSQL; - -namespace Api.Data.PostgreSQL.Data; - -/// -public class PostgreSqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local deleted file mode 100644 index e3fe4a49..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.Designer.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.Designer.cs deleted file mode 100644 index 898f949a..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.Designer.cs +++ /dev/null @@ -1,770 +0,0 @@ -// -using System; -using Api.Data.PostgreSQL.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Api.Data.PostgreSQL.Migrations -{ - [DbContext(typeof(PostgreSqlDbContext))] - [Migration("20260415145934_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleCreatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatable"); - }); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleCreatableAndEditable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatableAndEditable"); - }); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleDeletable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleDeletable"); - }); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleUpdatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleUpdatable"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("text"); - - b.Property("Xml") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityKey") - .HasColumnType("uuid"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("text"); - - b.Property("OldValue") - .HasColumnType("text"); - - b.Property("ParentId") - .HasColumnType("uuid"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("text"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ApiKeyId") - .HasColumnType("uuid"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ApiKeyId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.cs deleted file mode 100644 index 85e4a0fb..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.cs +++ /dev/null @@ -1,672 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Api.Data.PostgreSQL.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:PostgresExtension:postgis", ",,"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - CreatedBy = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - EntityKey = table.Column(type: "uuid", nullable: false), - EntitySetName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EntityTypeName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - EntityState = table.Column(type: "integer", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - FriendlyName = table.Column(type: "text", nullable: true), - Xml = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IsActive = table.Column(type: "boolean", nullable: false, defaultValue: true), - UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "boolean", nullable: false), - PasswordHash = table.Column(type: "text", nullable: true), - SecurityStamp = table.Column(type: "text", nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true), - PhoneNumber = table.Column(type: "text", nullable: true), - PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), - TwoFactorEnabled = table.Column(type: "boolean", nullable: false), - LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), - LockoutEnabled = table.Column(type: "boolean", nullable: false), - AccessFailedCount = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleCreatable", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleCreatable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleCreatableAndEditable", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleCreatableAndEditable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleDeletable", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleDeletable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleUpdatable", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleUpdatable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ParentId = table.Column(type: "uuid", nullable: false), - PropertyName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - RelationName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NewValue = table.Column(type: "text", nullable: true), - OldValue = table.Column(type: "text", nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - RoleId = table.Column(type: "uuid", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - Hash = table.Column(type: "text", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - RevokedAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - NewEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NewPhoneNumber = table.Column(type: "character varying(20)", maxLength: 20, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - UserId = table.Column(type: "uuid", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "text", nullable: false), - ProviderKey = table.Column(type: "text", nullable: false), - ProviderDisplayName = table.Column(type: "text", nullable: true), - UserId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - AppId = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - Value = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - ExpireAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "uuid", nullable: false), - RoleId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "uuid", nullable: false), - LoginProvider = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ApiKeyId = table.Column(type: "uuid", nullable: false), - ClaimType = table.Column(type: "text", nullable: false), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ApiKeyId = table.Column(type: "uuid", nullable: false), - RoleId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_CreatedAt", - table: "ExampleCreatable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_IsDeleted", - table: "ExampleCreatable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_Name", - table: "ExampleCreatable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_CreatedAt", - table: "ExampleCreatableAndEditable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_IsDeleted", - table: "ExampleCreatableAndEditable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_Name", - table: "ExampleCreatableAndEditable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_CreatedAt", - table: "ExampleDeletable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_IsDeleted", - table: "ExampleDeletable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_Name", - table: "ExampleDeletable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_CreatedAt", - table: "ExampleUpdatable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_IsDeleted", - table: "ExampleUpdatable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_Name", - table: "ExampleUpdatable", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "ExampleCreatable"); - - migrationBuilder.DropTable( - name: "ExampleCreatableAndEditable"); - - migrationBuilder.DropTable( - name: "ExampleDeletable"); - - migrationBuilder.DropTable( - name: "ExampleUpdatable"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs deleted file mode 100644 index 055a4aa4..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,767 +0,0 @@ -// -using System; -using Api.Data.PostgreSQL.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Api.Data.PostgreSQL.Migrations -{ - [DbContext(typeof(PostgreSqlDbContext))] - partial class PostgreSqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleCreatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatable"); - }); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleCreatableAndEditable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatableAndEditable"); - }); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleDeletable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleDeletable"); - }); - - modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleUpdatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleUpdatable"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("text"); - - b.Property("Xml") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityKey") - .HasColumnType("uuid"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("text"); - - b.Property("OldValue") - .HasColumnType("text"); - - b.Property("ParentId") - .HasColumnType("uuid"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("text"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ApiKeyId") - .HasColumnType("uuid"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ApiKeyId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs deleted file mode 100644 index 7c7e2ead..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.PostgreSQL.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.PostgreSQL; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Properties/InternalsVisibleTo.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 3690d0ad..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.PostgreSQL")] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Development.json b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Development.json deleted file mode 100644 index ddba6f26..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Production.json b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Staging.json b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.json b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.json deleted file mode 100644 index 824b3ae1..00000000 --- a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - "EvaluationInterval": 10, - "FailureNotificationInterval": 60, - "MaximumHistoryEntriesPerEndpoint": 50 - }, - "Documentation": { - "Name": "Application" - } - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "StartupAction": "None", - "UseLazyLoading": false, - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": true, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } -} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Dockerfile b/Api.Data.PostgreSQL/Dockerfile deleted file mode 100644 index 754207ea..00000000 --- a/Api.Data.PostgreSQL/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/LICENSE b/Api.Data.PostgreSQL/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.PostgreSQL/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.PostgreSQL/README.md b/Api.Data.PostgreSQL/README.md deleted file mode 100644 index a1cc16f0..00000000 --- a/Api.Data.PostgreSQL/README.md +++ /dev/null @@ -1,211 +0,0 @@ -# Api.Data.PostgreSQL - -> _Nano API application with postgre sql data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from -the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). - -Additionally, the example shows how Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories) works along with the corresponding -entity controllers. For more information on controllers and how they are connected with entity models, see [Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#controllers). - -A data health check is configured to target the database. -Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#health-checks)**. - -Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed -here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing -the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. - -> 📖 Learn more about **[Nano.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.PostgreSQL)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -Also, an initial migration has been added to the project. - -```powershell -dotnet ef migrations add Initial --project Api.Data.PostgreSQL -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "StartupAction": "None", - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } -} -``` - -...and `appsettings.Development.json` - -```json -"Data": { - "StartupAction": "Migrate", - "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" -} -``` - -## Docker Compose -Added PostgreSQL as a service dependency in `docker-compose.yml`. - -```yaml -services: - api.data.postgresql: - depends_on: - - database - - database: - image: postgis/postgis:latest - ports: - - 5432:5432 - networks: - - network - environment: - POSTGRES_USER: sa - POSTGRES_PASSWORD: myPassword_123 - POSTGRES_DB: nanoDb -``` - -## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. - -```json -spec: - template: - spec: - containers: - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_HOST || secrets.STAGING_POSTGRE_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-postgre-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_NANO_DB_PASSWORD || secrets.STAGING_POSTGRE_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_USER || secrets.STAGING_POSTGRE_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_PASSWORD || secrets.STAGING_POSTGRE_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true - DATA_MIGRATION_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true -``` - -Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y postgresql-client - - $userExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -c "CREATE ROLE $env:DATA_USER WITH LOGIN PASSWORD '$env:DATA_PASSWORD';" - } - - $userDbExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userDbExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT CONNECT ON DATABASE $env:DATA_NAME TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT USAGE ON SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:DATA_USER;" - } -``` - -Last, the application connectionstring must be added in a secret in Kuberntes. The `Kubernetes Deploy` step has been updated with the following. - -```yaml -sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Api.Data.PostgreSQL/icon.png b/Api.Data.PostgreSQL/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/.docker/docker-compose.yml b/Api.Data.Repository.AutoSave/.docker/docker-compose.yml deleted file mode 100644 index 9b69f42b..00000000 --- a/Api.Data.Repository.AutoSave/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.repository.autosave: - image: api.data.repository.autosave - hostname: api-data-repository-autosave - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.Repository.AutoSave - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.Repository.AutoSave/.dockerignore b/Api.Data.Repository.AutoSave/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.Repository.AutoSave/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.Repository.AutoSave/.github/config/slack.yml b/Api.Data.Repository.AutoSave/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.Repository.AutoSave/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml b/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 0548cc4a..00000000 --- a/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.Repository.AutoSave - IMAGE_NAME: api.data.repository.autosave - SERVICE_NAME: api-data-repository-autosave - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/.gitignore b/Api.Data.Repository.AutoSave/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.Repository.AutoSave/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/.kubernetes/autoscaler.yaml b/Api.Data.Repository.AutoSave/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.Repository.AutoSave/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml b/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml b/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml deleted file mode 100644 index 5901ef66..00000000 --- a/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Repository.AutoSave/.kubernetes/service.yaml b/Api.Data.Repository.AutoSave/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.Repository.AutoSave/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Properties/DoNotParallelize.cs b/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Tests.Api.Data.Repository.AutoSave.csproj b/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Tests.Api.Data.Repository.AutoSave.csproj deleted file mode 100644 index 23e5ef1d..00000000 --- a/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Tests.Api.Data.Repository.AutoSave.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Example.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Example.cs deleted file mode 100644 index 5890bfd9..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Repository.AutoSave.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.sln b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.sln deleted file mode 100644 index 6a8de69c..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Repository.AutoSave.Models", "Api.Data.Repository.AutoSave.Models\Api.Data.Repository.AutoSave.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Repository.AutoSave", "Api.Data.Repository.AutoSave\Api.Data.Repository.AutoSave.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Repository.AutoSave", ".tests\Tests.Api.Data.Repository.AutoSave\Tests.Api.Data.Repository.AutoSave.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.csproj b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.csproj deleted file mode 100644 index c2313257..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Controllers/ExamplesController.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Controllers/ExamplesController.cs deleted file mode 100644 index c2750aa1..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Controllers/ExamplesController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Api.Data.Repository.AutoSave.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace Api.Data.Repository.AutoSave.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository) -{ - /// - /// No Save Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("no-save")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task NoSaveAsync(CancellationToken cancellationToken = default) - { - await this.Repository - .AddAsync(new Example - { - Name = "name" - }, cancellationToken); - - return this.Ok("no-save"); - } -} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/Mappings/ExampleMapping.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index cd6d7970..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.Repository.AutoSave.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.Repository.AutoSave.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContext.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContext.cs deleted file mode 100644 index 459fd7ec..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.Repository.AutoSave.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContextFactory.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 8c372d43..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.Repository.AutoSave.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local deleted file mode 100644 index 265ee7c3..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.Repository.AutoSave.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.Designer.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.Designer.cs deleted file mode 100644 index a9a96364..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.Designer.cs +++ /dev/null @@ -1,651 +0,0 @@ -// -using System; -using Api.Data.Repository.AutoSave.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Repository.AutoSave.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415150030_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Repository.AutoSave.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.cs deleted file mode 100644 index 35d6f304..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.Repository.AutoSave.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 8b37879c..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,648 +0,0 @@ -// -using System; -using Api.Data.Repository.AutoSave.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Repository.AutoSave.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Repository.AutoSave.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Program.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Program.cs deleted file mode 100644 index a94ed0a2..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.Repository.AutoSave.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Properties/InternalsVisibleTo.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 1ba7f8ee..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.Repository.AutoSave")] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Production.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Staging.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.json deleted file mode 100644 index 91802788..00000000 --- a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null, - "Repository": { - "UseAutoSave": false - } - } -} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Dockerfile b/Api.Data.Repository.AutoSave/Dockerfile deleted file mode 100644 index 3c02c42d..00000000 --- a/Api.Data.Repository.AutoSave/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.Repository.AutoSave.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/LICENSE b/Api.Data.Repository.AutoSave/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.Repository.AutoSave/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/README.md b/Api.Data.Repository.AutoSave/README.md deleted file mode 100644 index 2d1998da..00000000 --- a/Api.Data.Repository.AutoSave/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# Api.Data.Repository.AutoSave - -> _Nano API application with data repository autosave disabled._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to -demonstrate repository autosave. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. - -In this application, autosave has been disabled. When the endpoint is invoked, Nano attempts to persist the entity to the database. However, the `IRepository` does -not commit the changes because autosave is disabled and `SaveChangesAsync(...)` is not invoked manually within the controller action. As a result, the changes are not -written to the database. If you switch the configuration to enable autosave, you will see that Nano then saves the changes. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ---------------------------------------------- | -------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/no-save` | Returns a simple `200 OK` response, while trying to add a new `Example`, changes are never saved. | - -> 📖 Learn more about **[Nano Data Repository Autosave](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#autosave)**. - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "Repository": { - "UseAutoSave": false - } -} -``` diff --git a/Api.Data.Repository.AutoSave/icon.png b/Api.Data.Repository.AutoSave/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.Repository.Includes/.docker/docker-compose.yml b/Api.Data.Repository.Includes/.docker/docker-compose.yml deleted file mode 100644 index 98336e73..00000000 --- a/Api.Data.Repository.Includes/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.repository.includes: - image: api.data.repository.includes - hostname: api-data-repository-includes - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.Repository.Includes - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.Repository.Includes/.dockerignore b/Api.Data.Repository.Includes/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.Repository.Includes/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.Repository.Includes/.github/config/slack.yml b/Api.Data.Repository.Includes/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.Repository.Includes/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml b/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml deleted file mode 100644 index f931bd34..00000000 --- a/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.Repository.Includes - IMAGE_NAME: api.data.repository.includes - SERVICE_NAME: api-data-repository-includes - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/.gitignore b/Api.Data.Repository.Includes/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.Repository.Includes/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Repository.Includes/.kubernetes/autoscaler.yaml b/Api.Data.Repository.Includes/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.Repository.Includes/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Repository.Includes/.kubernetes/configmap.yaml b/Api.Data.Repository.Includes/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.Repository.Includes/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Repository.Includes/.kubernetes/deployment.yaml b/Api.Data.Repository.Includes/.kubernetes/deployment.yaml deleted file mode 100644 index 5901ef66..00000000 --- a/Api.Data.Repository.Includes/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Repository.Includes/.kubernetes/service.yaml b/Api.Data.Repository.Includes/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.Repository.Includes/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Properties/DoNotParallelize.cs b/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Tests.Api.Data.Repository.Includes.csproj b/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Tests.Api.Data.Repository.Includes.csproj deleted file mode 100644 index 423e9dd5..00000000 --- a/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Tests.Api.Data.Repository.Includes.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Customer.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Customer.cs deleted file mode 100644 index 13472729..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Customer.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Annotations; -using Nano.Data.Abstractions.Config.Enums; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Repository.Includes.Models; - -/// -/// Customer. -/// -public class Customer : BaseEntity -{ - /// - /// Profile Id. - /// - [Required] - public virtual Guid ProfileId { get; set; } - - /// - /// Profile. - /// - public virtual CustomerProfile Profile { get; set; } = null!; - - /// - /// Order. - /// - [Required] - [Include(QuerySplitBehavior.SplitQuery)] - public virtual ICollection Orders { get; set; } = []; -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/CustomerProfile.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/CustomerProfile.cs deleted file mode 100644 index 5f0746e6..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/CustomerProfile.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Repository.Includes.Models; - -/// -/// Customer Profile. -/// -public class CustomerProfile : BaseEntity -{ - /// - /// Customer. - /// - public virtual Customer Customer { get; set; } = null!; - - /// - /// Name. - /// - [Required] - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Order.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Order.cs deleted file mode 100644 index 2b813b8f..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Order.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Annotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Repository.Includes.Models; - -/// -/// Order. -/// -public class Order : BaseEntity -{ - /// - /// Customer Id. - /// - [Required] - public virtual Guid CustomerId { get; set; } - - /// - /// Customer. - /// - public virtual Customer Customer { get; set; } = null!; - - /// - /// Payment Id. - /// - public virtual Guid? PaymentId { get; set; } - - /// - /// Payment. - /// - [Include] - public virtual Payment? Payment { get; set; } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Payment.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Payment.cs deleted file mode 100644 index d45920c6..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Payment.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Repository.Includes.Models; - -/// -/// Payment. -/// -public class Payment : BaseEntity -{ - /// - /// Order. - /// - public virtual Order Order { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.sln b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.sln deleted file mode 100644 index 61bcc364..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Repository.Includes.Models", "Api.Data.Repository.Includes.Models\Api.Data.Repository.Includes.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Repository.Includes", "Api.Data.Repository.Includes\Api.Data.Repository.Includes.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Repository.Includes", ".tests\Tests.Api.Data.Repository.Includes\Tests.Api.Data.Repository.Includes.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Api.Data.Repository.Includes.csproj b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Api.Data.Repository.Includes.csproj deleted file mode 100644 index 70ea6563..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Api.Data.Repository.Includes.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/ExamplesController.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/ExamplesController.cs deleted file mode 100644 index 2ae0ebfd..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/ExamplesController.cs +++ /dev/null @@ -1,146 +0,0 @@ -using Api.Data.Repository.Includes.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.Data.Repository.Includes.Controllers.Response; - -namespace Api.Data.Repository.Includes.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository) -{ - /// - /// Create Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpPost] - [Route("create")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task CreateAsync(CancellationToken cancellationToken = default) - { - await this.CreateCustomerAsync(cancellationToken); - } - - /// - /// Include Action. - /// - /// The include-depth. - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("include")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task IncludeAsync([FromQuery] int? includeDepth, CancellationToken cancellationToken = default) - { - var customer = await this.Repository - .GetFirstAsync(x => true, includeDepth, cancellationToken); - - return this.Ok(customer); - } - - /// - /// Create And Include Action. - /// - /// The include-depth. - /// The cancellation token. - /// A Cusetomer. - /// Success. - [HttpGet] - [Route("create-and-include")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task CreateAndIncludeAsync([FromQuery]int? includeDepth, CancellationToken cancellationToken = default) - { - var customer = await this.CreateCustomerAsync(cancellationToken); - - customer = await this.Repository - .GetAsync(customer.Id, includeDepth, cancellationToken); - - return this.Ok(customer); - } - - /// - /// Not Include Action. - /// - /// The include-depth. - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("not-include")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task NotIncludeAsync([FromQuery] int? includeDepth, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - var customer = new CustomerResponse - { - Id = Guid.NewGuid(), - Profile = new CustomerProfileResponse - { - Id = Guid.NewGuid(), - Name = "name" - }, - Orders = new List - { - new() - { - Id = Guid.NewGuid(), - Payment = new PaymentResponse - { - Id = Guid.NewGuid() - } - }, - new() - { - Id = Guid.NewGuid(), - Payment = new PaymentResponse - { - Id = Guid.NewGuid() - } - } - } - }; - - return this.Ok(customer); - } - - - private async Task CreateCustomerAsync(CancellationToken cancellationToken = default) - { - var customer = await this.Repository - .AddAsync(new Customer - { - Profile = new CustomerProfile - { - Name = "name" - }, - Orders = new List - { - new() - { - Payment = new Payment() - }, - new() - { - Payment = new Payment() - } - } - }, cancellationToken); - - return customer; - } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerProfileResponse.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerProfileResponse.cs deleted file mode 100644 index 2a88f64c..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerProfileResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; - -namespace Api.Data.Repository.Includes.Controllers.Response; - -/// -/// Customer Profile Response. -/// -public class CustomerProfileResponse -{ - /// - /// Id. - /// - public virtual Guid Id { get; set; } - - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerResponse.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerResponse.cs deleted file mode 100644 index 5e64558c..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerResponse.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Api.Data.Repository.Includes.Controllers.Response; - -/// -/// Customer Response. -/// -public class CustomerResponse -{ - /// - /// Id. - /// - public virtual Guid Id { get; set; } - - /// - /// Profile. - /// - public virtual CustomerProfileResponse Profile { get; set; } = null!; - - /// - /// Order. - /// - public virtual ICollection Orders { get; set; } = []; -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/OrderResponse.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/OrderResponse.cs deleted file mode 100644 index 7bf83f1e..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/OrderResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace Api.Data.Repository.Includes.Controllers.Response; - -/// -/// Order Response. -/// -public class OrderResponse -{ - /// - /// Id. - /// - public virtual Guid Id { get; set; } - - /// - /// Customer. - /// - public virtual CustomerResponse Customer { get; set; } = null!; - - /// - /// Payment. - /// - public virtual PaymentResponse? Payment { get; set; } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/PaymentResponse.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/PaymentResponse.cs deleted file mode 100644 index 22afcbb5..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/PaymentResponse.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; - -namespace Api.Data.Repository.Includes.Controllers.Response; - -/// -/// Payment Response. -/// -public class PaymentResponse -{ - /// - /// Id. - /// - public virtual Guid Id { get; set; } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerMapping.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerMapping.cs deleted file mode 100644 index 16e0a485..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerMapping.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Api.Data.Repository.Includes.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.Repository.Includes.Data.Mappings; - -/// -/// Customer Mapping. -/// -public class CustomerMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .HasOne(x => x.Profile) - .WithOne(x => x.Customer) - .IsRequired(); - - builder - .HasMany(x => x.Orders) - .WithOne(x => x.Customer) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerProfileMapping.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerProfileMapping.cs deleted file mode 100644 index d61d7b62..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerProfileMapping.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Api.Data.Repository.Includes.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.Repository.Includes.Data.Mappings; - -/// -/// Customer Profile Mapping. -/// -public class CustomerProfileMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .HasOne(x => x.Customer) - .WithOne(x => x.Profile); - - builder - .Property(x => x.Name) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/OrderMapping.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/OrderMapping.cs deleted file mode 100644 index d2bc2cc8..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/OrderMapping.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Api.Data.Repository.Includes.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.Repository.Includes.Data.Mappings; - -/// -/// Example Mapping. -/// -public class OrderMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .HasOne(x => x.Customer) - .WithMany(x => x.Orders) - .IsRequired(); - - builder - .HasOne(x => x.Payment) - .WithOne(x => x.Order); - } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/PaymentMapping.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/PaymentMapping.cs deleted file mode 100644 index b2d147ed..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/PaymentMapping.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Api.Data.Repository.Includes.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.Repository.Includes.Data.Mappings; - -/// -/// Example Mapping. -/// -public class PaymentMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .HasOne(x => x.Order) - .WithOne(x => x.Payment); - } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContext.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContext.cs deleted file mode 100644 index b34dbcf7..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.Repository.Includes.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContextFactory.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 92a54df6..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.Repository.Includes.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local deleted file mode 100644 index f67686a8..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.Repository.Includes.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.Designer.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.Designer.cs deleted file mode 100644 index 37f28f9a..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.Designer.cs +++ /dev/null @@ -1,791 +0,0 @@ -// -using System; -using Api.Data.Repository.Includes.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Repository.Includes.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415150006_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("ProfileId") - .IsUnique() - .HasDatabaseName("UX_Customer_ProfileId"); - - b.ToTable("Customer"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.CustomerProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("CustomerProfile"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("CustomerId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("PaymentId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("CustomerId"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("PaymentId") - .IsUnique() - .HasDatabaseName("UX_Order_PaymentId"); - - b.ToTable("Order"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Payment"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => - { - b.HasOne("Api.Data.Repository.Includes.Models.CustomerProfile", "Profile") - .WithOne("Customer") - .HasForeignKey("Api.Data.Repository.Includes.Models.Customer", "ProfileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Order", b => - { - b.HasOne("Api.Data.Repository.Includes.Models.Customer", "Customer") - .WithMany("Orders") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Api.Data.Repository.Includes.Models.Payment", "Payment") - .WithOne("Order") - .HasForeignKey("Api.Data.Repository.Includes.Models.Order", "PaymentId"); - - b.Navigation("Customer"); - - b.Navigation("Payment"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => - { - b.Navigation("Orders"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.CustomerProfile", b => - { - b.Navigation("Customer") - .IsRequired(); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Payment", b => - { - b.Navigation("Order") - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.cs deleted file mode 100644 index a0cad7c7..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.cs +++ /dev/null @@ -1,717 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.Repository.Includes.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "CustomerProfile", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_CustomerProfile", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Payment", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Payment", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Customer", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ProfileId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Customer", x => x.Id); - table.ForeignKey( - name: "FK_Customer_CustomerProfile_ProfileId", - column: x => x.ProfileId, - principalTable: "CustomerProfile", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Order", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CustomerId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PaymentId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Order", x => x.Id); - table.ForeignKey( - name: "FK_Order_Customer_CustomerId", - column: x => x.CustomerId, - principalTable: "Customer", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_Order_Payment_PaymentId", - column: x => x.PaymentId, - principalTable: "Payment", - principalColumn: "Id"); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Customer_CreatedAt", - table: "Customer", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Customer_IsDeleted", - table: "Customer", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "UX_Customer_ProfileId", - table: "Customer", - column: "ProfileId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_CustomerProfile_CreatedAt", - table: "CustomerProfile", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_CustomerProfile_IsDeleted", - table: "CustomerProfile", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Order_CreatedAt", - table: "Order", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Order_CustomerId", - table: "Order", - column: "CustomerId"); - - migrationBuilder.CreateIndex( - name: "IX_Order_IsDeleted", - table: "Order", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "UX_Order_PaymentId", - table: "Order", - column: "PaymentId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Payment_CreatedAt", - table: "Payment", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Payment_IsDeleted", - table: "Payment", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Order"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "Customer"); - - migrationBuilder.DropTable( - name: "Payment"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - - migrationBuilder.DropTable( - name: "CustomerProfile"); - } - } -} diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 7028a6e8..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,788 +0,0 @@ -// -using System; -using Api.Data.Repository.Includes.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Repository.Includes.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("ProfileId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("ProfileId") - .IsUnique() - .HasDatabaseName("UX_Customer_ProfileId"); - - b.ToTable("Customer"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.CustomerProfile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("CustomerProfile"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Order", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("CustomerId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("PaymentId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("CustomerId"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("PaymentId") - .IsUnique() - .HasDatabaseName("UX_Order_PaymentId"); - - b.ToTable("Order"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Payment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("Payment"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => - { - b.HasOne("Api.Data.Repository.Includes.Models.CustomerProfile", "Profile") - .WithOne("Customer") - .HasForeignKey("Api.Data.Repository.Includes.Models.Customer", "ProfileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Order", b => - { - b.HasOne("Api.Data.Repository.Includes.Models.Customer", "Customer") - .WithMany("Orders") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Api.Data.Repository.Includes.Models.Payment", "Payment") - .WithOne("Order") - .HasForeignKey("Api.Data.Repository.Includes.Models.Order", "PaymentId"); - - b.Navigation("Customer"); - - b.Navigation("Payment"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => - { - b.Navigation("Orders"); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.CustomerProfile", b => - { - b.Navigation("Customer") - .IsRequired(); - }); - - modelBuilder.Entity("Api.Data.Repository.Includes.Models.Payment", b => - { - b.Navigation("Order") - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Program.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Program.cs deleted file mode 100644 index 133b1047..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.Repository.Includes.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Properties/InternalsVisibleTo.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 93b5d5b4..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.Repository.Includes")] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Production.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Staging.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.json deleted file mode 100644 index d52d2638..00000000 --- a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null, - "Repository": { - "QueryIncludeDepth": 3 - } - } -} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Dockerfile b/Api.Data.Repository.Includes/Dockerfile deleted file mode 100644 index 51c6edab..00000000 --- a/Api.Data.Repository.Includes/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.Repository.Includes.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/LICENSE b/Api.Data.Repository.Includes/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.Repository.Includes/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Repository.Includes/README.md b/Api.Data.Repository.Includes/README.md deleted file mode 100644 index cf9fb5d2..00000000 --- a/Api.Data.Repository.Includes/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Api.Data.Repository.Includes - -> _Nano API application with data repository include annotations._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to -demonstrate repository include annotations. Entity controllers have been simplified to showcase include annotation; full controllers are unnecessary. - -The endpoints can be invoked with the `includeDepth` query parameter to override the default configured depth. This makes it easy to experiment with different values -and observe how the returned object graph changes as Nano resolves deeper levels of includes. - -All the navigations in the object graph is annotated with `IncludeAttribute`, except for `Customer.Profile`. Because of this, it is not exposed during -response serialization, even that the instance is already loaded in the data context. Only properties explicitly marked for inclusion are serialized in the response. You -can read more about this behavior in the [Response Serialization](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#response-serialization) section. - -Observe how includes and nested includes appear in the response after the entities have been created and subsequently retrieved -using `IRepository.GetAsync(...)`. This demonstrates how Nano automatically resolves and serializes the configured include graph according to the effective depth. - -Another endpoint returns plain objects that do not implement `IEntity`, so the full object graph is always serialized. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/create` | Returns a simple `200 OK` response. Creates a Customer entity with related navigations. | -| `http://localhost:8080/api/examples/include` | Returns a simple `200 OK` response with `Customer` entity and nested included navigation properties. Requires a `Cusetomer` entity to be created first. | -| `http://localhost:8080/api/examples/create-and-include` | Returns a simple `200 OK` response. Creates a `Customer` entity and nested included navigation properties, and returns it. ⚠️ If request `includeDepth` is lower than configuration, serialization still exposes the depth using the confoguration. | -| `http://localhost:8080/api/examples/not-include` | Returns a simple `200 OK` response with `CustomerResponse`, that is not `IEntity`, and all properties are serialzied and exposed. | - -> 📖 Learn more about **[Nano Include Annotation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#include-annotation)**. - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "Repository": { - "QueryIncludeDepth": 3 - } -} -``` diff --git a/Api.Data.Repository.Includes/icon.png b/Api.Data.Repository.Includes/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.SoftDelete/.docker/docker-compose.yml b/Api.Data.SoftDelete/.docker/docker-compose.yml deleted file mode 100644 index 2182ed3f..00000000 --- a/Api.Data.SoftDelete/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.repository.softdelete: - image: api.data.repository.softdelete - hostname: api-data-softdelete - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.SoftDelete - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.SoftDelete/.dockerignore b/Api.Data.SoftDelete/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.SoftDelete/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.SoftDelete/.github/config/slack.yml b/Api.Data.SoftDelete/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.SoftDelete/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml b/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 8633fdfb..00000000 --- a/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.SoftDelete - IMAGE_NAME: api.data.softdelete - SERVICE_NAME: api-data-softdelete - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.SoftDelete/.gitignore b/Api.Data.SoftDelete/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.SoftDelete/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.SoftDelete/.kubernetes/autoscaler.yaml b/Api.Data.SoftDelete/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.SoftDelete/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.SoftDelete/.kubernetes/configmap.yaml b/Api.Data.SoftDelete/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.SoftDelete/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.SoftDelete/.kubernetes/deployment.yaml b/Api.Data.SoftDelete/.kubernetes/deployment.yaml deleted file mode 100644 index 5901ef66..00000000 --- a/Api.Data.SoftDelete/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.SoftDelete/.kubernetes/service.yaml b/Api.Data.SoftDelete/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.SoftDelete/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Properties/DoNotParallelize.cs b/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Tests.Api.Data.SoftDelete.csproj b/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Tests.Api.Data.SoftDelete.csproj deleted file mode 100644 index 33c551d0..00000000 --- a/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Tests.Api.Data.SoftDelete.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index b5bce1e9..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.SoftDelete.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Example.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Example.cs deleted file mode 100644 index d9c21ef1..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Example.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Nano.Data.Abstractions.Models; -using Nano.Data.Abstractions.Models.Abstractions; - -namespace Api.Data.SoftDelete.Models; - -/// -/// Example. -/// -public class Example : BaseEntity, IEntitySoftDeletable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.sln b/Api.Data.SoftDelete/Api.Data.SoftDelete.sln deleted file mode 100644 index 07ee2aa8..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SoftDelete.Models", "Api.Data.SoftDelete.Models\Api.Data.SoftDelete.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SoftDelete", "Api.Data.SoftDelete\Api.Data.SoftDelete.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.SoftDelete", ".tests\Tests.Api.Data.SoftDelete\Tests.Api.Data.SoftDelete.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Api.Data.SoftDelete.csproj b/Api.Data.SoftDelete/Api.Data.SoftDelete/Api.Data.SoftDelete.csproj deleted file mode 100644 index 03912446..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Api.Data.SoftDelete.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Controllers/ExamplesController.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Controllers/ExamplesController.cs deleted file mode 100644 index 545fb607..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SoftDelete.Models; -using Api.Data.SoftDelete.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SoftDelete.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/Mappings/ExampleMapping.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index f16a7a45..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using Api.Data.SoftDelete.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; -using Nano.Data.Mappings.Extensions; - -namespace Api.Data.SoftDelete.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - - builder - .OnDeleting(x => - { - Console.WriteLine("OnDeleting"); - }); - - builder - .OnDeleted(x => - { - Console.WriteLine("OnDeleted"); - }); - } -} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContext.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContext.cs deleted file mode 100644 index e046a986..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.SoftDelete.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContextFactory.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 2ab2f480..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.SoftDelete.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local b/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local deleted file mode 100644 index f0398733..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.SoftDelete.dll"] \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.Designer.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.Designer.cs deleted file mode 100644 index ac78cc0c..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.Designer.cs +++ /dev/null @@ -1,651 +0,0 @@ -// -using System; -using Api.Data.SoftDelete.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.SoftDelete.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415151003_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.SoftDelete.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.cs deleted file mode 100644 index a2ec8ea4..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.cs +++ /dev/null @@ -1,601 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.SoftDelete.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index 4f38b424..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,648 +0,0 @@ -// -using System; -using Api.Data.SoftDelete.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.SoftDelete.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.SoftDelete.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Program.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Program.cs deleted file mode 100644 index 25e7035f..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.SoftDelete.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Properties/InternalsVisibleTo.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 9a763dd3..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.SoftDelete")] \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Production.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Staging.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.json deleted file mode 100644 index d43a066a..00000000 --- a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Dockerfile b/Api.Data.SoftDelete/Dockerfile deleted file mode 100644 index 4c606a9e..00000000 --- a/Api.Data.SoftDelete/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.SoftDelete.dll"] \ No newline at end of file diff --git a/Api.Data.SoftDelete/LICENSE b/Api.Data.SoftDelete/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.SoftDelete/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.SoftDelete/README.md b/Api.Data.SoftDelete/README.md deleted file mode 100644 index 1a984ed0..00000000 --- a/Api.Data.SoftDelete/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Api.Data.SoftDelete - -> _Nano API application with data soft delete._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to -demonstrate soft delete. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. - -The `Example` entity implements `IEntitySoftDeletable`, so when an entity is deleted, it is not removed from the database but is marked as deleted -by setting the `IsDeleted` property. - -The data mapping also includes two triggers for `OnDeleting` and `OnDeleted` to show that they are invoked also when soft-deleting entity models. - -Open the database and notice that the created `Example` entity has a non-zero `IsDeleted` value, indicating it has been soft-deleted. - -> 📖 Learn more about **[Nano Data Soft Delete](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#soft-delete)**. diff --git a/Api.Data.SoftDelete/icon.png b/Api.Data.SoftDelete/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.SqLite/.docker/docker-compose.yml b/Api.Data.SqLite/.docker/docker-compose.yml deleted file mode 100644 index 53243c0b..00000000 --- a/Api.Data.SqLite/.docker/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - api.data.sqlite: - image: api.data.postgresql.spatial - hostname: api-data-postgresql-spatial - build: - context: ../Api.Data.SqLite - dockerfile: Dockerfile.Local - hostname: api-data-sqlite - restart: on-failure - ports: - - 8080:8080 - volumes: - - ./bin/data:/data - networks: - - network - -networks: - network: - driver: bridge \ No newline at end of file diff --git a/Api.Data.SqLite/.dockerignore b/Api.Data.SqLite/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.SqLite/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.SqLite/.github/config/slack.yml b/Api.Data.SqLite/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.SqLite/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.SqLite/.github/workflows/build-and-deploy.yml b/Api.Data.SqLite/.github/workflows/build-and-deploy.yml deleted file mode 100644 index e008225d..00000000 --- a/Api.Data.SqLite/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,201 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.SqLite - IMAGE_NAME: api.data.sqlite - SERVICE_NAME: api-data-sqlite - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_NAME: nanoDb - DATA_SIZE: 10Gi - DATA_MIGRATION_CONNECTIONSTRING: "Data Source=/data/{{ env.nanoDb }}.sqlite" -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/data-storageclass.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/data-storageclass.tmp.yaml; - sudo kubectl apply -f .kubernetes/data-storageclass.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/data-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/data-pvc.tmp.yaml; - sudo kubectl apply -f .kubernetes/data-pvc.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.SqLite/.gitignore b/Api.Data.SqLite/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.SqLite/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.SqLite/.kubernetes/autoscaler.yaml b/Api.Data.SqLite/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.SqLite/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.SqLite/.kubernetes/configmap.yaml b/Api.Data.SqLite/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.SqLite/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.SqLite/.kubernetes/data-pvc.yaml b/Api.Data.SqLite/.kubernetes/data-pvc.yaml deleted file mode 100644 index 0b9d4415..00000000 --- a/Api.Data.SqLite/.kubernetes/data-pvc.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: %SERVICE_NAME%-data-pvc -spec: - accessModes: - - ReadWriteOnce - storageClassName: %SERVICE_NAME%-data-storage-class - resources: - requests: - storage: %DATA_SIZE% \ No newline at end of file diff --git a/Api.Data.SqLite/.kubernetes/data-storageclass.yaml b/Api.Data.SqLite/.kubernetes/data-storageclass.yaml deleted file mode 100644 index 47530cdc..00000000 --- a/Api.Data.SqLite/.kubernetes/data-storageclass.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: %SERVICE_NAME%-data-storage-class -provisioner: kubernetes.io/azure-disk -parameters: - storageaccounttype: Standard_LRS - kind: Managed -reclaimPolicy: Retain -volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/Api.Data.SqLite/.kubernetes/deployment.yaml b/Api.Data.SqLite/.kubernetes/deployment.yaml deleted file mode 100644 index 0b6637e4..00000000 --- a/Api.Data.SqLite/.kubernetes/deployment.yaml +++ /dev/null @@ -1,85 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.SqLite/.kubernetes/service.yaml b/Api.Data.SqLite/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.SqLite/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Properties/DoNotParallelize.cs b/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Tests.Api.Data.SqLite.csproj b/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Tests.Api.Data.SqLite.csproj deleted file mode 100644 index 0ebcb00b..00000000 --- a/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Tests.Api.Data.SqLite.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj b/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj deleted file mode 100644 index 796675d7..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 0898a54e..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.SqLite.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/Example.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/Example.cs deleted file mode 100644 index 8ec5fc89..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqLite.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatable.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatable.cs deleted file mode 100644 index 5674fe49..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqLite.Models; - -/// -/// Example. -/// -public class ExampleCreatable : BaseEntityCreatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatableAndEditable.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatableAndEditable.cs deleted file mode 100644 index b79faaf8..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatableAndEditable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqLite.Models; - -/// -/// Example. -/// -public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleDeletable.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleDeletable.cs deleted file mode 100644 index ea738af4..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleDeletable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqLite.Models; - -/// -/// Example. -/// -public class ExampleDeletable : BaseEntityDeletable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleUpdatable.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleUpdatable.cs deleted file mode 100644 index acf1a748..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleUpdatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqLite.Models; - -/// -/// Example. -/// -public class ExampleUpdatable : BaseEntityUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.sln b/Api.Data.SqLite/Api.Data.SqLite.sln deleted file mode 100644 index 4466c201..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite.sln +++ /dev/null @@ -1,149 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\data-pvc.yaml = .kubernetes\data-pvc.yaml - .kubernetes\data-storageclass.yaml = .kubernetes\data-storageclass.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqLite.Models", "Api.Data.SqLite.Models\Api.Data.SqLite.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqLite", "Api.Data.SqLite\Api.Data.SqLite.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.SqLite", ".tests\Tests.Api.Data.SqLite\Tests.Api.Data.SqLite.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqLite", "..\..\Nano.Library\Nano.Data.SqLite\Nano.Data.SqLite.csproj", "{0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.SqLite/Api.Data.SqLite/Api.Data.SqLite.csproj b/Api.Data.SqLite/Api.Data.SqLite/Api.Data.SqLite.csproj deleted file mode 100644 index ac14956f..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Api.Data.SqLite.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreatableAndEditableController.cs deleted file mode 100644 index 23b765ca..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreatableAndEditableController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqLite.Models; -using Api.Data.SqLite.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqLite.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) - : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreateablesController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreateablesController.cs deleted file mode 100644 index 4e4a20a5..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreateablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqLite.Models; -using Api.Data.SqLite.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqLite.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreateablesController(ILogger logger, IRepository repository) - : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleDeletablesController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleDeletablesController.cs deleted file mode 100644 index 816442f3..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleDeletablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqLite.Models; -using Api.Data.SqLite.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqLite.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleDeletablesController(ILogger logger, IRepository repository) - : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleUpdatablesController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleUpdatablesController.cs deleted file mode 100644 index 4eb1e08b..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleUpdatablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqLite.Models; -using Api.Data.SqLite.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqLite.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleUpdatablesController(ILogger logger, IRepository repository) - : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExamplesController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExamplesController.cs deleted file mode 100644 index c7d426ba..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqLite.Models; -using Api.Data.SqLite.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqLite.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs deleted file mode 100644 index 04319331..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqLite.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqLite.Data.Mappings; - -/// -/// Example Creatable And Updatable Mapping. -/// -public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableMapping.cs deleted file mode 100644 index 45a6bd26..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqLite.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqLite.Data.Mappings; - -/// -/// Example Creatable Mapping. -/// -public class ExampleCreatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleDeletableMapping.cs deleted file mode 100644 index 0eee93ec..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleDeletableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqLite.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqLite.Data.Mappings; - -/// -/// Example Deletable Mapping. -/// -public class ExampleDeletableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 82a1f096..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqLite.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqLite.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleUpdatableMapping.cs deleted file mode 100644 index 3849ad5a..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqLite.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqLite.Data.Mappings; - -/// -/// Example Updatable Mapping. -/// -public class ExampleUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/MySqlDbContextFactory.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 6545c502..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.SqLite; - -namespace Api.Data.SqLite.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContext.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContext.cs deleted file mode 100644 index 9726d363..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.SqLite.Data; - -/// -public class SqLiteDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local b/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local deleted file mode 100644 index 542f0ed1..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local +++ /dev/null @@ -1,10 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -RUN apt-get update \ - && apt-get install -y libsqlite3-mod-spatialite \ - && apt-get install -y libspatialite-dev - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.SqLite.dll"] \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260415151010_Initial.Designer.cs b/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260415151010_Initial.Designer.cs deleted file mode 100644 index 61eabdcd..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260415151010_Initial.Designer.cs +++ /dev/null @@ -1,758 +0,0 @@ -// -using System; -using Api.Data.SqLite.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.SqLite.Migrations -{ - [DbContext(typeof(SqLiteDbContext))] - [Migration("20260415151010_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.11"); - - modelBuilder.Entity("Api.Data.SqLite.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.SqLite.Models.ExampleCreatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatable"); - }); - - modelBuilder.Entity("Api.Data.SqLite.Models.ExampleCreatableAndEditable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatableAndEditable"); - }); - - modelBuilder.Entity("Api.Data.SqLite.Models.ExampleDeletable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleDeletable"); - }); - - modelBuilder.Entity("Api.Data.SqLite.Models.ExampleUpdatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleUpdatable"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("FriendlyName") - .HasColumnType("TEXT"); - - b.Property("Xml") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("ProviderKey") - .HasColumnType("TEXT"); - - b.Property("ProviderDisplayName") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EntityKey") - .HasColumnType("TEXT"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .HasColumnType("INTEGER"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .HasColumnType("INTEGER"); - - b.Property("NewValue") - .HasColumnType("TEXT"); - - b.Property("OldValue") - .HasColumnType("TEXT"); - - b.Property("ParentId") - .HasColumnType("TEXT"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("RevokedAt") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ApiKeyId") - .HasColumnType("TEXT"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ApiKeyId") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AccessFailedCount") - .HasColumnType("INTEGER"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EmailConfirmed") - .HasColumnType("INTEGER"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("INTEGER"); - - b.Property("LockoutEnd") - .HasColumnType("TEXT"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PhoneNumber") - .HasColumnType("TEXT"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("INTEGER"); - - b.Property("SecurityStamp") - .HasColumnType("TEXT"); - - b.Property("TwoFactorEnabled") - .HasColumnType("INTEGER"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("ExpireAt") - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260415151010_Initial.cs b/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260415151010_Initial.cs deleted file mode 100644 index 5096c776..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260415151010_Initial.cs +++ /dev/null @@ -1,668 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.SqLite.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - CreatedBy = table.Column(type: "TEXT", maxLength: 256, nullable: false), - EntityKey = table.Column(type: "TEXT", nullable: false), - EntitySetName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - EntityTypeName = table.Column(type: "TEXT", maxLength: 256, nullable: false), - EntityState = table.Column(type: "INTEGER", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "TEXT", maxLength: 256, nullable: true), - IsDeleted = table.Column(type: "INTEGER", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - FriendlyName = table.Column(type: "TEXT", nullable: true), - Xml = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - IsActive = table.Column(type: "INTEGER", nullable: false, defaultValue: true), - UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "INTEGER", nullable: false), - PasswordHash = table.Column(type: "TEXT", nullable: true), - SecurityStamp = table.Column(type: "TEXT", nullable: true), - ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), - PhoneNumber = table.Column(type: "TEXT", nullable: true), - PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), - TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), - LockoutEnd = table.Column(type: "TEXT", nullable: true), - LockoutEnabled = table.Column(type: "INTEGER", nullable: false), - AccessFailedCount = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleCreatable", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleCreatable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleCreatableAndEditable", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleCreatableAndEditable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleDeletable", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleDeletable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleUpdatable", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleUpdatable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - ParentId = table.Column(type: "TEXT", nullable: false), - PropertyName = table.Column(type: "TEXT", maxLength: 256, nullable: false), - RelationName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NewValue = table.Column(type: "TEXT", nullable: true), - OldValue = table.Column(type: "TEXT", nullable: true), - IsDeleted = table.Column(type: "INTEGER", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - RoleId = table.Column(type: "TEXT", nullable: false), - ClaimType = table.Column(type: "TEXT", nullable: true), - ClaimValue = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - IdentityUserId = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", maxLength: 256, nullable: false), - Hash = table.Column(type: "TEXT", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false), - RevokedAt = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - IdentityUserId = table.Column(type: "TEXT", nullable: false), - NewEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NewPhoneNumber = table.Column(type: "TEXT", maxLength: 20, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - UserId = table.Column(type: "TEXT", nullable: false), - ClaimType = table.Column(type: "TEXT", nullable: true), - ClaimValue = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "TEXT", nullable: false), - ProviderKey = table.Column(type: "TEXT", nullable: false), - ProviderDisplayName = table.Column(type: "TEXT", nullable: true), - UserId = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - IdentityUserId = table.Column(type: "TEXT", nullable: false), - AppId = table.Column(type: "TEXT", maxLength: 256, nullable: false), - Value = table.Column(type: "TEXT", maxLength: 256, nullable: false), - ExpireAt = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "TEXT", nullable: false), - RoleId = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "TEXT", nullable: false), - LoginProvider = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - Value = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - ApiKeyId = table.Column(type: "TEXT", nullable: false), - ClaimType = table.Column(type: "TEXT", nullable: false), - ClaimValue = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - ApiKeyId = table.Column(type: "TEXT", nullable: false), - RoleId = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_CreatedAt", - table: "ExampleCreatable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_IsDeleted", - table: "ExampleCreatable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_Name", - table: "ExampleCreatable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_CreatedAt", - table: "ExampleCreatableAndEditable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_IsDeleted", - table: "ExampleCreatableAndEditable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_Name", - table: "ExampleCreatableAndEditable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_CreatedAt", - table: "ExampleDeletable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_IsDeleted", - table: "ExampleDeletable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_Name", - table: "ExampleDeletable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_CreatedAt", - table: "ExampleUpdatable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_IsDeleted", - table: "ExampleUpdatable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_Name", - table: "ExampleUpdatable", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "ExampleCreatable"); - - migrationBuilder.DropTable( - name: "ExampleCreatableAndEditable"); - - migrationBuilder.DropTable( - name: "ExampleDeletable"); - - migrationBuilder.DropTable( - name: "ExampleUpdatable"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.SqLite/Api.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs b/Api.Data.SqLite/Api.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs deleted file mode 100644 index bc3c35c9..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs +++ /dev/null @@ -1,755 +0,0 @@ -// -using System; -using Api.Data.SqLite.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.SqLite.Migrations -{ - [DbContext(typeof(SqLiteDbContext))] - partial class SqLiteDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.11"); - - modelBuilder.Entity("Api.Data.SqLite.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.SqLite.Models.ExampleCreatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatable"); - }); - - modelBuilder.Entity("Api.Data.SqLite.Models.ExampleCreatableAndEditable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatableAndEditable"); - }); - - modelBuilder.Entity("Api.Data.SqLite.Models.ExampleDeletable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleDeletable"); - }); - - modelBuilder.Entity("Api.Data.SqLite.Models.ExampleUpdatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleUpdatable"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("FriendlyName") - .HasColumnType("TEXT"); - - b.Property("Xml") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("ProviderKey") - .HasColumnType("TEXT"); - - b.Property("ProviderDisplayName") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EntityKey") - .HasColumnType("TEXT"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .HasColumnType("INTEGER"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .HasColumnType("INTEGER"); - - b.Property("NewValue") - .HasColumnType("TEXT"); - - b.Property("OldValue") - .HasColumnType("TEXT"); - - b.Property("ParentId") - .HasColumnType("TEXT"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("RevokedAt") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ApiKeyId") - .HasColumnType("TEXT"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ApiKeyId") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AccessFailedCount") - .HasColumnType("INTEGER"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EmailConfirmed") - .HasColumnType("INTEGER"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("INTEGER"); - - b.Property("LockoutEnd") - .HasColumnType("TEXT"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PhoneNumber") - .HasColumnType("TEXT"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("INTEGER"); - - b.Property("SecurityStamp") - .HasColumnType("TEXT"); - - b.Property("TwoFactorEnabled") - .HasColumnType("INTEGER"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("ExpireAt") - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SqLite/Api.Data.SqLite/Program.cs b/Api.Data.SqLite/Api.Data.SqLite/Program.cs deleted file mode 100644 index 933859f3..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.SqLite.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.SqLite; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.SqLite/Api.Data.SqLite/Properties/InternalsVisibleTo.cs b/Api.Data.SqLite/Api.Data.SqLite/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 13fd80d2..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.SqLite")] \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json deleted file mode 100644 index 1e8d984a..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - } -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Production.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Staging.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.json deleted file mode 100644 index 8fd62bfb..00000000 --- a/Api.Data.SqLite/Api.Data.SqLite/appsettings.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - "EvaluationInterval": 10, - "FailureNotificationInterval": 60, - "MaximumHistoryEntriesPerEndpoint": 50 - }, - "Documentation": { - "Name": "Application" - } - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "StartupAction": "None", - "UseLazyLoading": false, - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": "Data Source=/data/nanoDb.sqlite", - "Repository": { - "UseAutoSave": true, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } -} \ No newline at end of file diff --git a/Api.Data.SqLite/Dockerfile b/Api.Data.SqLite/Dockerfile deleted file mode 100644 index 93565b0d..00000000 --- a/Api.Data.SqLite/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -RUN apt-get update \ - && apt-get install -y libsqlite3-mod-spatialite \ - && apt-get install -y libspatialite-dev - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.SqLite.dll"] \ No newline at end of file diff --git a/Api.Data.SqLite/LICENSE b/Api.Data.SqLite/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.SqLite/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.SqLite/README.md b/Api.Data.SqLite/README.md deleted file mode 100644 index b77374f6..00000000 --- a/Api.Data.SqLite/README.md +++ /dev/null @@ -1,175 +0,0 @@ -# Api.Data.SqLite - -> _Nano API application with sqlite data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from -the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). - -Additionally, the example shows how Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories) works along with the corresponding -entity controllers. For more information on controllers and how they are connected with entity models, see [Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#controllers). - -A data health check is configured to target the database. -Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#health-checks)**. - -Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed -here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing -the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. - -> 📖 Learn more about **[Nano.Data.SqLite](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqLite)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -Also, an initial migration has been added to the project. - -```powershell -dotnet ef migrations add Initial --project Api.Data.SqLite -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "StartupAction": "None", - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": "Data Source=/data/nanoDb.sqlite", - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } -} -``` - -...and `appsettings.Development.json` - -```json -"Data": { - "StartupAction": "Migrate" -} -``` - -## Docker Compose -Added SqLite as a service dependency in `docker-compose.yml`. - -```yaml -services: - api.data.sqlite: - volumes: - - ./bin/data:/data -``` - -Also the `Dockerfile` must have SqLite installed with spatial support. Add the following to both the `Dockerfile` and the `Dockerfile.Local`. - -```dockerfile -RUN apt-get update \ - && apt-get install -y libsqlite3-mod-spatialite \ - && apt-get install -y libspatialite-dev -``` - -## Kubernetes -Added two additional kubernetes templates, `storageclass.yaml` and `pvc.yaml`, for dynamically manage and creating the disk for the SqLite database. - -Also, updated `deployment.yaml` adding the volumes and volume mounts. - -```json -spec: - template: - spec: - containers: - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - DATA_NAME: nanoDb - DATA_SIZE: 10Gi - DATA_CONNECTIONSTRING: "Data Source=/data/{{ env.nanoDb }}.sqlite" -``` - -Additionally, this step has been added to ensure database migrations are applied. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; -``` - -Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Api.Data.SqLite/icon.png b/Api.Data.SqLite/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/.docker/docker-compose.yml b/Api.Data.SqlServer.Spatial/.docker/docker-compose.yml deleted file mode 100644 index 82e79e94..00000000 --- a/Api.Data.SqlServer.Spatial/.docker/docker-compose.yml +++ /dev/null @@ -1,30 +0,0 @@ -services: - api.data.sqlserver.spatial: - image: api.data.sqlserver.spatial - hostname: api-data-sqlserver-spatial - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.SqlServer.Spatial - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mcr.microsoft.com/mssql/server:2022-latest - ports: - - 1433:1433 - networks: - - network - environment: - SA_PASSWORD: myPassword_123 - ACCEPT_EULA: Y - MSSQL_PID: Developer - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.SqlServer.Spatial/.dockerignore b/Api.Data.SqlServer.Spatial/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.SqlServer.Spatial/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.SqlServer.Spatial/.github/config/slack.yml b/Api.Data.SqlServer.Spatial/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.SqlServer.Spatial/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 827b1d7a..00000000 --- a/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,239 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.SqlServer.Spatial - IMAGE_NAME: api.data.sqlserver.spatial - SERVICE_NAME: api-data-sqlserver-spatial - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_HOST || secrets.STAGING_SQLSERVER_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-sqlserver-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_NANO_DB_PASSWORD || secrets.STAGING_SQLSERVER_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_USER || secrets.STAGING_SQLSERVER_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_PASSWORD || secrets.STAGING_SQLSERVER_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }};Encrypt=True;TrustServerCertificate=True; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }};Encrypt=True;TrustServerCertificate=True; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mssql-tools unixodbc-dev - - $loginExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:DATA_USER';" - - if ($loginExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -Q "CREATE LOGIN [$env:DATA_USER] WITH PASSWORD = '$env:DATA_PASSWORD';" - }; - - $userExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:DATA_USER';" - - if ($userExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -Q "CREATE USER [$env:DATA_USER] FOR LOGIN [$env:DATA_USER]; - ALTER ROLE db_datareader ADD MEMBER [$env:DATA_USER]; - ALTER ROLE db_datawriter ADD MEMBER [$env:DATA_USER];" - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/.gitignore b/Api.Data.SqlServer.Spatial/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.SqlServer.Spatial/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/autoscaler.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.SqlServer.Spatial/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/service.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.SqlServer.Spatial/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Properties/DoNotParallelize.cs b/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Tests.Api.Data.SqlServer.Spatial.csproj b/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Tests.Api.Data.SqlServer.Spatial.csproj deleted file mode 100644 index edb960c3..00000000 --- a/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Tests.Api.Data.SqlServer.Spatial.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj deleted file mode 100644 index 8c31e7ba..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index ad1ace22..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; -using NetTopologySuite.Geometries; - -namespace Api.Data.SqlServer.Spatial.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Point. - /// - public virtual Point? Point { get; set; } - - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (this.Point != null) - { - expression - .IsWithinDistance(nameof(Example.Point), this.Point, 10000); - } - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Example.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Example.cs deleted file mode 100644 index 8cd0560e..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Example.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; -using NetTopologySuite.Geometries; - -namespace Api.Data.SqlServer.Spatial.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Point. - /// - [Required] - public virtual Point Point { get; set; } = null!; - - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.sln b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.sln deleted file mode 100644 index cd2ba3a3..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqlServer.Spatial.Models", "Api.Data.SqlServer.Spatial.Models\Api.Data.SqlServer.Spatial.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqlServer.Spatial", "Api.Data.SqlServer.Spatial\Api.Data.SqlServer.Spatial.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.SqlServer.Spatial", ".tests\Tests.Api.Data.SqlServer.Spatial\Tests.Api.Data.SqlServer.Spatial.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqlServer", "..\..\Nano.Library\Nano.Data.SqlServer\Nano.Data.SqlServer.csproj", "{FE8B04D5-8AED-F023-588B-65947B83FDF5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {FE8B04D5-8AED-F023-588B-65947B83FDF5} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.csproj b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.csproj deleted file mode 100644 index 7ca03113..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Controllers/ExamplesController.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Controllers/ExamplesController.cs deleted file mode 100644 index a0ed166d..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqlServer.Spatial.Models; -using Api.Data.SqlServer.Spatial.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqlServer.Spatial.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/Mappings/ExampleMapping.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 5552f03c..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using Api.Data.SqlServer.Spatial.Models; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqlServer.Spatial.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Point) - .HasColumnType("geography") - .IsRequired(); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContext.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContext.cs deleted file mode 100644 index 3bc5cbf4..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.SqlServer.Spatial.Data; - -/// -public class SqlServerDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContextFactory.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContextFactory.cs deleted file mode 100644 index f9b9b8b9..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.SqlServer; - -namespace Api.Data.SqlServer.Spatial.Data; - -/// -public class SqlServerDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local deleted file mode 100644 index cb81de7f..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.SqlServer.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.Designer.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.Designer.cs deleted file mode 100644 index 946d8122..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.Designer.cs +++ /dev/null @@ -1,658 +0,0 @@ -// -using System; -using Api.Data.SqlServer.Spatial.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using NetTopologySuite.Geometries; - -#nullable disable - -namespace Api.Data.SqlServer.Spatial.Migrations -{ - [DbContext(typeof(SqlServerDbContext))] - [Migration("20260415151456_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.SqlServer.Spatial.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Point") - .IsRequired() - .HasColumnType("geography"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("nvarchar(max)"); - - b.Property("Xml") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityKey") - .HasColumnType("uniqueidentifier"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("nvarchar(max)"); - - b.Property("OldValue") - .HasColumnType("nvarchar(max)"); - - b.Property("ParentId") - .HasColumnType("uniqueidentifier"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(450)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique() - .HasFilter("[Email] IS NOT NULL"); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.HasIndex("PhoneNumber") - .IsUnique() - .HasFilter("[PhoneNumber] IS NOT NULL"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetimeoffset"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.cs deleted file mode 100644 index 5340a112..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.cs +++ /dev/null @@ -1,546 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using NetTopologySuite.Geometries; - -#nullable disable - -namespace Api.Data.SqlServer.Spatial.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - CreatedBy = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - EntityKey = table.Column(type: "uniqueidentifier", nullable: false), - EntitySetName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EntityTypeName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - FriendlyName = table.Column(type: "nvarchar(max)", nullable: true), - Xml = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "bit", nullable: false), - PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), - SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumber = table.Column(type: "nvarchar(450)", nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false), - LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), - LockoutEnabled = table.Column(type: "bit", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Point = table.Column(type: "geography", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ParentId = table.Column(type: "uniqueidentifier", nullable: false), - PropertyName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - RelationName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NewValue = table.Column(type: "nvarchar(max)", nullable: true), - OldValue = table.Column(type: "nvarchar(max)", nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Hash = table.Column(type: "nvarchar(max)", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false), - RevokedAt = table.Column(type: "datetimeoffset", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - NewEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NewPhoneNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), - ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), - UserId = table.Column(type: "uniqueidentifier", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - AppId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Value = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - ExpireAt = table.Column(type: "datetimeoffset", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - RoleId = table.Column(type: "uniqueidentifier", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - Value = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(450)", nullable: false), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), - RoleId = table.Column(type: "uniqueidentifier", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true, - filter: "[NormalizedName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true, - filter: "[Email] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true, - filter: "[PhoneNumber] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true, - filter: "[NormalizedUserName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.Designer.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.Designer.cs deleted file mode 100644 index b5459320..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.Designer.cs +++ /dev/null @@ -1,658 +0,0 @@ -// -using System; -using Api.Data.SqlServer.Spatial.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using NetTopologySuite.Geometries; - -#nullable disable - -namespace Api.Data.SqlServer.Spatial.Migrations -{ - [DbContext(typeof(SqlServerDbContext))] - [Migration("20260415151538_AddedSpatialIndex")] - partial class AddedSpatialIndex - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.SqlServer.Spatial.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Point") - .IsRequired() - .HasColumnType("geography"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("nvarchar(max)"); - - b.Property("Xml") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityKey") - .HasColumnType("uniqueidentifier"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("nvarchar(max)"); - - b.Property("OldValue") - .HasColumnType("nvarchar(max)"); - - b.Property("ParentId") - .HasColumnType("uniqueidentifier"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(450)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique() - .HasFilter("[Email] IS NOT NULL"); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.HasIndex("PhoneNumber") - .IsUnique() - .HasFilter("[PhoneNumber] IS NOT NULL"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetimeoffset"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.cs deleted file mode 100644 index 6908b76e..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.SqlServer.Spatial.Migrations -{ - /// - public partial class AddedSpatialIndex : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder - .Sql(@"CREATE SPATIAL INDEX IX_Example_Point - ON Example(Point) - USING GEOGRAPHY_GRID"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - - } - } -} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/SqlServerDbContextModelSnapshot.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/SqlServerDbContextModelSnapshot.cs deleted file mode 100644 index ac476593..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/SqlServerDbContextModelSnapshot.cs +++ /dev/null @@ -1,655 +0,0 @@ -// -using System; -using Api.Data.SqlServer.Spatial.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using NetTopologySuite.Geometries; - -#nullable disable - -namespace Api.Data.SqlServer.Spatial.Migrations -{ - [DbContext(typeof(SqlServerDbContext))] - partial class SqlServerDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.SqlServer.Spatial.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("Point") - .IsRequired() - .HasColumnType("geography"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("nvarchar(max)"); - - b.Property("Xml") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityKey") - .HasColumnType("uniqueidentifier"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("nvarchar(max)"); - - b.Property("OldValue") - .HasColumnType("nvarchar(max)"); - - b.Property("ParentId") - .HasColumnType("uniqueidentifier"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(450)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique() - .HasFilter("[Email] IS NOT NULL"); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.HasIndex("PhoneNumber") - .IsUnique() - .HasFilter("[PhoneNumber] IS NOT NULL"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetimeoffset"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Program.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Program.cs deleted file mode 100644 index f044c3d8..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.SqlServer.Spatial.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.SqlServer; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Properties/InternalsVisibleTo.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 1557b1b7..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.SqlServer.Spatial")] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Development.json b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Development.json deleted file mode 100644 index 960866dc..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Production.json b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Staging.json b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.json b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.json deleted file mode 100644 index a5c27531..00000000 --- a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - "EvaluationInterval": 10, - "FailureNotificationInterval": 60, - "MaximumHistoryEntriesPerEndpoint": 50 - }, - "Documentation": { - "Name": "Application" - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Dockerfile b/Api.Data.SqlServer.Spatial/Dockerfile deleted file mode 100644 index 1bc24530..00000000 --- a/Api.Data.SqlServer.Spatial/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.SqlServer.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/LICENSE b/Api.Data.SqlServer.Spatial/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.SqlServer.Spatial/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/README.md b/Api.Data.SqlServer.Spatial/README.md deleted file mode 100644 index 7ed6c218..00000000 --- a/Api.Data.SqlServer.Spatial/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Api.Data.SqlServer.Spatial - -> _Nano API application with sql server spatial data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Migrations](#migrations) - -## Summary -This application builds on **[Api.Data.SqlServer](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.SqlServer)**. Entity controllers have been simplified to -showcase spatial types; full controllers are unnecessary. - -The `Example` entity now includes a `Point` property from `NetTopologySuite`. A query criterion has been added to check whether points are within a 10,000 meter distance. The -entity mappings for this spatial property have also been configured. Otherwise, no other changes were made. - -> ⚠️ Be aware, SQL Server does not create spatial indexes automatically; they must be added manually in a migration. - -```csharp -migrationBuilder - .Sql(@"CREATE SPATIAL INDEX IX_Example_Point - ON Example(Point) - USING GEOGRAPHY_GRID"); -``` - -> 📖 Learn more about **[Nano.Data.SqlServer](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqlServer)**. diff --git a/Api.Data.SqlServer.Spatial/icon.png b/Api.Data.SqlServer.Spatial/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.SqlServer/.docker/docker-compose.yml b/Api.Data.SqlServer/.docker/docker-compose.yml deleted file mode 100644 index 33a18108..00000000 --- a/Api.Data.SqlServer/.docker/docker-compose.yml +++ /dev/null @@ -1,30 +0,0 @@ -services: - api.data.sqlserver: - image: api.data.sqlserver - hostname: api-data-sqlserver - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.SqlServer - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mcr.microsoft.com/mssql/server:2022-latest - ports: - - 1433:1433 - networks: - - network - environment: - SA_PASSWORD: myPassword_123 - ACCEPT_EULA: Y - MSSQL_PID: Developer - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.SqlServer/.dockerignore b/Api.Data.SqlServer/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.SqlServer/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.SqlServer/.github/config/slack.yml b/Api.Data.SqlServer/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.SqlServer/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml b/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 913a983e..00000000 --- a/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,239 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.SqlServer - IMAGE_NAME: api.data.sqlserver - SERVICE_NAME: api-data-sqlserver - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_HOST || secrets.STAGING_SQLSERVER_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-sqlserver-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_NANO_DB_PASSWORD || secrets.STAGING_SQLSERVER_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_USER || secrets.STAGING_SQLSERVER_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_PASSWORD || secrets.STAGING_SQLSERVER_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }};Encrypt=True;TrustServerCertificate=True; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }};Encrypt=True;TrustServerCertificate=True; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mssql-tools unixodbc-dev - - $loginExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:DATA_USER';" - - if ($loginExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -Q "CREATE LOGIN [$env:DATA_USER] WITH PASSWORD = '$env:DATA_PASSWORD';" - }; - - $userExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:DATA_USER';" - - if ($userExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -Q "CREATE USER [$env:DATA_USER] FOR LOGIN [$env:DATA_USER]; - ALTER ROLE db_datareader ADD MEMBER [$env:DATA_USER]; - ALTER ROLE db_datawriter ADD MEMBER [$env:DATA_USER];" - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.SqlServer/.gitignore b/Api.Data.SqlServer/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.SqlServer/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.SqlServer/.kubernetes/autoscaler.yaml b/Api.Data.SqlServer/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.SqlServer/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.SqlServer/.kubernetes/configmap.yaml b/Api.Data.SqlServer/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.SqlServer/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.SqlServer/.kubernetes/deployment.yaml b/Api.Data.SqlServer/.kubernetes/deployment.yaml deleted file mode 100644 index 560e3901..00000000 --- a/Api.Data.SqlServer/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.SqlServer/.kubernetes/service.yaml b/Api.Data.SqlServer/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.SqlServer/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Properties/DoNotParallelize.cs b/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Tests.Api.Data.SqlServer.csproj b/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Tests.Api.Data.SqlServer.csproj deleted file mode 100644 index 33f34658..00000000 --- a/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Tests.Api.Data.SqlServer.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj deleted file mode 100644 index 8c31e7ba..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index 0129030d..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.SqlServer.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - /// Name. - /// - public virtual string? Name { get; set; } - - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); - - if (!string.IsNullOrEmpty(this.Name)) - { - expression - .StartsWith(nameof(Example.Name), this.Name); - } - - expressions - .Add(expression); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Example.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Example.cs deleted file mode 100644 index 0c053260..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqlServer.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatable.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatable.cs deleted file mode 100644 index a3fe275d..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqlServer.Models; - -/// -/// Example. -/// -public class ExampleCreatable : BaseEntityCreatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatableAndEditable.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatableAndEditable.cs deleted file mode 100644 index 6a459d38..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatableAndEditable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqlServer.Models; - -/// -/// Example. -/// -public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleDeletable.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleDeletable.cs deleted file mode 100644 index 8c458f93..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleDeletable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqlServer.Models; - -/// -/// Example. -/// -public class ExampleDeletable : BaseEntityDeletable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleUpdatable.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleUpdatable.cs deleted file mode 100644 index ce1959ab..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleUpdatable.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Api.Data.SqlServer.Models; - -/// -/// Example. -/// -public class ExampleUpdatable : BaseEntityUpdatable -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.sln b/Api.Data.SqlServer/Api.Data.SqlServer.sln deleted file mode 100644 index 4c4fa9e3..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqlServer.Models", "Api.Data.SqlServer.Models\Api.Data.SqlServer.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqlServer", "Api.Data.SqlServer\Api.Data.SqlServer.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.SqlServer", ".tests\Tests.Api.Data.SqlServer\Tests.Api.Data.SqlServer.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqlServer", "..\..\Nano.Library\Nano.Data.SqlServer\Nano.Data.SqlServer.csproj", "{FE8B04D5-8AED-F023-588B-65947B83FDF5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {FE8B04D5-8AED-F023-588B-65947B83FDF5} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Api.Data.SqlServer.csproj b/Api.Data.SqlServer/Api.Data.SqlServer/Api.Data.SqlServer.csproj deleted file mode 100644 index 56bec247..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Api.Data.SqlServer.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreatableAndEditableController.cs deleted file mode 100644 index 8f509aa5..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreatableAndEditableController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqlServer.Models; -using Api.Data.SqlServer.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqlServer.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) - : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreateablesController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreateablesController.cs deleted file mode 100644 index 7cf1f57b..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreateablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqlServer.Models; -using Api.Data.SqlServer.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqlServer.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleCreateablesController(ILogger logger, IRepository repository) - : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleDeletablesController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleDeletablesController.cs deleted file mode 100644 index 4fdeda31..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleDeletablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqlServer.Models; -using Api.Data.SqlServer.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqlServer.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleDeletablesController(ILogger logger, IRepository repository) - : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleUpdatablesController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleUpdatablesController.cs deleted file mode 100644 index 3ea0f3c6..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleUpdatablesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqlServer.Models; -using Api.Data.SqlServer.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqlServer.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExampleUpdatablesController(ILogger logger, IRepository repository) - : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExamplesController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExamplesController.cs deleted file mode 100644 index 279ec99b..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.SqlServer.Models; -using Api.Data.SqlServer.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.SqlServer.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs deleted file mode 100644 index 67adeef4..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqlServer.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqlServer.Data.Mappings; - -/// -/// Example Creatable And Updatable Mapping. -/// -public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableMapping.cs deleted file mode 100644 index 66317926..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqlServer.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqlServer.Data.Mappings; - -/// -/// Example Creatable Mapping. -/// -public class ExampleCreatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleDeletableMapping.cs deleted file mode 100644 index c35832c9..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleDeletableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqlServer.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqlServer.Data.Mappings; - -/// -/// Example Deletable Mapping. -/// -public class ExampleDeletableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index c840122b..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqlServer.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqlServer.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleUpdatableMapping.cs deleted file mode 100644 index 2183ff74..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleUpdatableMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Api.Data.SqlServer.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.SqlServer.Data.Mappings; - -/// -/// Example Updatable Mapping. -/// -public class ExampleUpdatableMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContext.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContext.cs deleted file mode 100644 index 3c12e60e..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.SqlServer.Data; - -/// -public class SqlServerDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContextFactory.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContextFactory.cs deleted file mode 100644 index 2d206aea..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.SqlServer; - -namespace Api.Data.SqlServer.Data; - -/// -public class SqlServerDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local b/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local deleted file mode 100644 index 8297641c..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.Designer.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.Designer.cs deleted file mode 100644 index 770c02a9..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.Designer.cs +++ /dev/null @@ -1,773 +0,0 @@ -// -using System; -using Api.Data.SqlServer.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.SqlServer.Migrations -{ - [DbContext(typeof(SqlServerDbContext))] - [Migration("20260415151054_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.SqlServer.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleCreatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatable"); - }); - - modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleCreatableAndEditable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatableAndEditable"); - }); - - modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleDeletable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleDeletable"); - }); - - modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleUpdatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleUpdatable"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("nvarchar(max)"); - - b.Property("Xml") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityKey") - .HasColumnType("uniqueidentifier"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("nvarchar(max)"); - - b.Property("OldValue") - .HasColumnType("nvarchar(max)"); - - b.Property("ParentId") - .HasColumnType("uniqueidentifier"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(450)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique() - .HasFilter("[Email] IS NOT NULL"); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.HasIndex("PhoneNumber") - .IsUnique() - .HasFilter("[PhoneNumber] IS NOT NULL"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetimeoffset"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.cs deleted file mode 100644 index fa54f6f7..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.cs +++ /dev/null @@ -1,672 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.SqlServer.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - CreatedBy = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - EntityKey = table.Column(type: "uniqueidentifier", nullable: false), - EntitySetName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EntityTypeName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - FriendlyName = table.Column(type: "nvarchar(max)", nullable: true), - Xml = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "bit", nullable: false), - PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), - SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumber = table.Column(type: "nvarchar(450)", nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false), - LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), - LockoutEnabled = table.Column(type: "bit", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleCreatable", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleCreatable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleCreatableAndEditable", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleCreatableAndEditable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleDeletable", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleDeletable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "ExampleUpdatable", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleUpdatable", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ParentId = table.Column(type: "uniqueidentifier", nullable: false), - PropertyName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - RelationName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NewValue = table.Column(type: "nvarchar(max)", nullable: true), - OldValue = table.Column(type: "nvarchar(max)", nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Hash = table.Column(type: "nvarchar(max)", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false), - RevokedAt = table.Column(type: "datetimeoffset", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - NewEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NewPhoneNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), - ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), - UserId = table.Column(type: "uniqueidentifier", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - AppId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Value = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - ExpireAt = table.Column(type: "datetimeoffset", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - RoleId = table.Column(type: "uniqueidentifier", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - Value = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(450)", nullable: false), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), - RoleId = table.Column(type: "uniqueidentifier", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true, - filter: "[NormalizedName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true, - filter: "[Email] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true, - filter: "[PhoneNumber] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true, - filter: "[NormalizedUserName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_CreatedAt", - table: "ExampleCreatable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_IsDeleted", - table: "ExampleCreatable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatable_Name", - table: "ExampleCreatable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_CreatedAt", - table: "ExampleCreatableAndEditable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_IsDeleted", - table: "ExampleCreatableAndEditable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleCreatableAndEditable_Name", - table: "ExampleCreatableAndEditable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_CreatedAt", - table: "ExampleDeletable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_IsDeleted", - table: "ExampleDeletable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleDeletable_Name", - table: "ExampleDeletable", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_CreatedAt", - table: "ExampleUpdatable", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_IsDeleted", - table: "ExampleUpdatable", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleUpdatable_Name", - table: "ExampleUpdatable", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "ExampleCreatable"); - - migrationBuilder.DropTable( - name: "ExampleCreatableAndEditable"); - - migrationBuilder.DropTable( - name: "ExampleDeletable"); - - migrationBuilder.DropTable( - name: "ExampleUpdatable"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs deleted file mode 100644 index daf3a185..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs +++ /dev/null @@ -1,770 +0,0 @@ -// -using System; -using Api.Data.SqlServer.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.SqlServer.Migrations -{ - [DbContext(typeof(SqlServerDbContext))] - partial class SqlServerDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.SqlServer.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleCreatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatable"); - }); - - modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleCreatableAndEditable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleCreatableAndEditable"); - }); - - modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleDeletable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleDeletable"); - }); - - modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleUpdatable", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("ExampleUpdatable"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("nvarchar(max)"); - - b.Property("Xml") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityKey") - .HasColumnType("uniqueidentifier"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("nvarchar(max)"); - - b.Property("OldValue") - .HasColumnType("nvarchar(max)"); - - b.Property("ParentId") - .HasColumnType("uniqueidentifier"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ApiKeyId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(450)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique() - .HasFilter("[Email] IS NOT NULL"); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.HasIndex("PhoneNumber") - .IsUnique() - .HasFilter("[PhoneNumber] IS NOT NULL"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetimeoffset"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Program.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Program.cs deleted file mode 100644 index d2643c8a..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.SqlServer.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.SqlServer; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Properties/InternalsVisibleTo.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 2b16073e..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.SqlServer")] \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Development.json b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Development.json deleted file mode 100644 index 960866dc..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Production.json b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Staging.json b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.json b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.json deleted file mode 100644 index 824b3ae1..00000000 --- a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - "EvaluationInterval": 10, - "FailureNotificationInterval": 60, - "MaximumHistoryEntriesPerEndpoint": 50 - }, - "Documentation": { - "Name": "Application" - } - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "StartupAction": "None", - "UseLazyLoading": false, - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": true, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } -} \ No newline at end of file diff --git a/Api.Data.SqlServer/Dockerfile b/Api.Data.SqlServer/Dockerfile deleted file mode 100644 index e140c4f8..00000000 --- a/Api.Data.SqlServer/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer/LICENSE b/Api.Data.SqlServer/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.SqlServer/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.SqlServer/README.md b/Api.Data.SqlServer/README.md deleted file mode 100644 index 3214928a..00000000 --- a/Api.Data.SqlServer/README.md +++ /dev/null @@ -1,222 +0,0 @@ -# Api.Data.SqlServer - -> _Nano API application with sql server data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from -the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). - -Additionally, the example shows how Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories) works along with the corresponding -entity controllers. For more information on controllers and how they are connected with entity models, see [Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#controllers). - -A data health check is configured to target the database. -Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#health-checks)**. - -Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed -here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing -the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. - -> 📖 Learn more about **[Nano.Data.SqlServer](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqlServer)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -Also, an initial migration has been added to the project. - -```powershell -dotnet ef migrations add Initial --project Api.Data.SqlServer -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "StartupAction": "None", - "UseSensitiveDataLogging": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } -} -``` - -...and `appsettings.Development.json` - -```json -"Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" -} -``` - -## Docker Compose -Added Sql Server as a service dependency in `docker-compose.yml`. - -```yaml -services: - api.data.sqlserver: - depends_on: - - database - - database: - image: mcr.microsoft.com/mssql/server:2022-latest - ports: - - 1433:1433 - networks: - - network - environment: - SA_PASSWORD: myPassword_123 - ACCEPT_EULA: Y - MSSQL_PID: Developer -``` - -## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. - -```json -spec: - template: - spec: - containers: - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_HOST || secrets.STAGING_SQLSERVER_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-sqlserver-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_NANO_DB_PASSWORD || secrets.STAGING_SQLSERVER_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_USER || secrets.STAGING_SQLSERVER_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_PASSWORD || secrets.STAGING_SQLSERVER_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }}; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }}; -``` - -Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mssql-tools unixodbc-dev - - $loginExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:DATA_USER';" - - if ($loginExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -Q "CREATE LOGIN [$env:DATA_USER] WITH PASSWORD = '$env:DATA_PASSWORD';" - } - - $userExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:DATA_USER';" - - if ($userExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -Q "CREATE USER [$env:DATA_USER] FOR LOGIN [$env:DATA_USER]; - ALTER ROLE db_datareader ADD MEMBER [$env:DATA_USER]; - ALTER ROLE db_datawriter ADD MEMBER [$env:DATA_USER];" - } -``` - -Last, the application connectionstring must be added in a secret in Kuberntes. The `Kubernetes Deploy` step has been updated with the following. - -```yaml -sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Api.Data.SqlServer/icon.png b/Api.Data.SqlServer/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Data.Triggers/.docker/docker-compose.yml b/Api.Data.Triggers/.docker/docker-compose.yml deleted file mode 100644 index 1bd3d4c3..00000000 --- a/Api.Data.Triggers/.docker/docker-compose.yml +++ /dev/null @@ -1,32 +0,0 @@ -services: - api.data.repository.triggers: - image: api.data.repository.triggers - hostname: api-data-triggers - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Data.Triggers - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - driver: bridge diff --git a/Api.Data.Triggers/.dockerignore b/Api.Data.Triggers/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Data.Triggers/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Data.Triggers/.github/config/slack.yml b/Api.Data.Triggers/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Data.Triggers/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Triggers/.github/workflows/build-and-deploy.yml b/Api.Data.Triggers/.github/workflows/build-and-deploy.yml deleted file mode 100644 index f1727498..00000000 --- a/Api.Data.Triggers/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,211 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Data.Triggers - IMAGE_NAME: api.data.triggers - SERVICE_NAME: api-data-triggers - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - MYSQL_DATABASE_NAME: nanoDb - MYSQL_DATABASE_USER: api-data-mysql-user - MYSQL_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - MYSQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - MYSQL_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - MYSQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - MYSQL_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_DATABASE_USER }};Pwd=${{ env.MYSQL_SERVICE_PASSWORD }};SslMode=Preferred; - MYSQL_MIGRATION_CONNECTIONSTRING: Server=${{ env.MYSQL_HOST }};Port=${{ vars.MYSQL_PORT }};Database=${{ env.MYSQL_DATABASE_NAME }};Uid=${{ env.MYSQL_ADMIN_USER }};Pwd=${{ env.MYSQL_ADMIN_PASSWORD }};SslMode=Preferred; -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:MYSQL_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:MYSQL_DATABASE_USER');" $env:MYSQL_MIGRATION_CONNECTIONSTRING - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:MYSQL_DATABASE_USER'@'%' IDENTIFIED BY '$env:MYSQL_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:MYSQL_DATABASE_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:MYSQL_MIGRATION_CONNECTIONSTRING - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Triggers/.gitignore b/Api.Data.Triggers/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Data.Triggers/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Triggers/.kubernetes/autoscaler.yaml b/Api.Data.Triggers/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Data.Triggers/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Triggers/.kubernetes/configmap.yaml b/Api.Data.Triggers/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Data.Triggers/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Triggers/.kubernetes/deployment.yaml b/Api.Data.Triggers/.kubernetes/deployment.yaml deleted file mode 100644 index 5901ef66..00000000 --- a/Api.Data.Triggers/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Data.Triggers/.kubernetes/service.yaml b/Api.Data.Triggers/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Data.Triggers/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Properties/DoNotParallelize.cs b/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Tests.Api.Data.Triggers.csproj b/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Tests.Api.Data.Triggers.csproj deleted file mode 100644 index 5e2fd720..00000000 --- a/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Tests.Api.Data.Triggers.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj b/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj deleted file mode 100644 index 82be9b94..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleQueryCriteria.cs deleted file mode 100644 index e9546b7e..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleQueryCriteria.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.Triggers.Models.Criterias; - -/// -public class ExampleQueryCriteria : BaseQueryCriteria -{ - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleTriggerQueryCriteria.cs b/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleTriggerQueryCriteria.cs deleted file mode 100644 index d401429f..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleTriggerQueryCriteria.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using DynamicExpression; -using Nano.App.Api.Controllers.Criteria; - -namespace Api.Data.Triggers.Models.Criterias; - -/// -public class ExampleTriggerQueryCriteria : BaseQueryCriteria -{ - /// - public override IList GetExpressions() - { - var expressions = base.GetExpressions(); - - return expressions; - } -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Example.cs b/Api.Data.Triggers/Api.Data.Triggers.Models/Example.cs deleted file mode 100644 index 4be5377c..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers.Models/Example.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Triggers.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - [Required] - public virtual string Name { get; set; } = null!; - - /// - /// Updated At. - /// - public virtual DateTimeOffset? UpdatedAt { get; set; } -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/ExampleTrigger.cs b/Api.Data.Triggers/Api.Data.Triggers.Models/ExampleTrigger.cs deleted file mode 100644 index bcb7c553..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers.Models/ExampleTrigger.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using Nano.Data.Abstractions.Models; - -namespace Api.Data.Triggers.Models; - -/// -/// Example Trigger. -/// -public class ExampleTrigger : BaseEntity -{ - /// - /// Example Id. - /// - [Required] - public virtual Guid ExampleId { get; set; } - - /// - /// Trigger. - /// - public virtual string Trigger { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.sln b/Api.Data.Triggers/Api.Data.Triggers.sln deleted file mode 100644 index c8a7d06c..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Triggers.Models", "Api.Data.Triggers.Models\Api.Data.Triggers.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Triggers", "Api.Data.Triggers\Api.Data.Triggers.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Triggers", ".tests\Tests.Api.Data.Triggers\Tests.Api.Data.Triggers.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Data.Triggers/Api.Data.Triggers/Api.Data.Triggers.csproj b/Api.Data.Triggers/Api.Data.Triggers/Api.Data.Triggers.csproj deleted file mode 100644 index f1828c23..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Api.Data.Triggers.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExampleTriggersController.cs b/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExampleTriggersController.cs deleted file mode 100644 index 4166e77f..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExampleTriggersController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.Triggers.Models; -using Api.Data.Triggers.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.Triggers.Controllers; - -/// -/// Controller with example triggers. -/// -/// The . -/// The . -public class ExampleTriggersController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExamplesController.cs b/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExamplesController.cs deleted file mode 100644 index 4476cba5..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExamplesController.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Api.Data.Triggers.Models; -using Api.Data.Triggers.Models.Criterias; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Data.Abstractions; - -namespace Api.Data.Triggers.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IRepository repository) - : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleMapping.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index c0849781..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using Api.Data.Triggers.Data.Triggers; -using Api.Data.Triggers.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; -using Nano.Data.Mappings.Extensions; - -namespace Api.Data.Triggers.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - - builder - .Property(x => x.UpdatedAt); - - builder - .HasIndex(x => x.UpdatedAt); - - builder - .OnInserting(ExampleTriggers.Inserting); - - builder - .OnInserted(ExampleTriggers.Inserted); - - builder - .OnUpdating(ExampleTriggers.Updating); - - builder - .OnUpdated(ExampleTriggers.Updated); - - builder - .OnDeleting(ExampleTriggers.Deleting); - - builder - .OnDeleted(ExampleTriggers.Deleted); - } -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleTriggerMapping.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleTriggerMapping.cs deleted file mode 100644 index 7bc3a37c..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleTriggerMapping.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Api.Data.Triggers.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Api.Data.Triggers.Data.Mappings; - -/// -/// Example Trigger Mapping. -/// -public class ExampleTriggerMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Trigger) - .IsRequired(); - - builder - .Property(x => x.ExampleId) - .IsRequired(); - } -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContext.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContext.cs deleted file mode 100644 index c85366c3..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Api.Data.Triggers.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContextFactory.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContextFactory.cs deleted file mode 100644 index a2ab5c49..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Api.Data.Triggers.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/Triggers/ExampleTriggers.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/Triggers/ExampleTriggers.cs deleted file mode 100644 index 42b8da92..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Data/Triggers/ExampleTriggers.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using Api.Data.Triggers.Models; -using EntityFrameworkCore.Triggers; - -namespace Api.Data.Triggers.Data.Triggers; - -internal static class ExampleTriggers -{ - internal static Action> Inserting = x => - { - x.Entity.UpdatedAt = DateTimeOffset.UtcNow; - - x.Context - .Add(new ExampleTrigger - { - ExampleId = x.Entity.Id, - Trigger = "OnInserting" - }); - }; - - internal static Action> Inserted = x => - { - x.Context - .Add(new ExampleTrigger - { - ExampleId = x.Entity.Id, - Trigger = "OnInserted" - }); - }; - - internal static Action> Updating = x => - { - x.Entity.UpdatedAt = DateTimeOffset.UtcNow; - - x.Context - .Add(new ExampleTrigger - { - ExampleId = x.Entity.Id, - Trigger = "OnUpdating" - }); - }; - - internal static Action> Updated = x => - { - x.Context - .Add(new ExampleTrigger - { - ExampleId = x.Entity.Id, - Trigger = "OnUpdated" - }); - }; - - internal static Action> Deleting = x => - { - x.Context - .Add(new ExampleTrigger - { - ExampleId = x.Entity.Id, - Trigger = "OnDeleting" - }); - }; - - internal static Action> Deleted = x => - { - x.Context - .Add(new ExampleTrigger - { - ExampleId = x.Entity.Id, - Trigger = "OnDeleted" - }); - }; -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local b/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local deleted file mode 100644 index b8b7fc64..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Data.Triggers.dll"] \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.Designer.cs b/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.Designer.cs deleted file mode 100644 index 030ada5a..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.Designer.cs +++ /dev/null @@ -1,689 +0,0 @@ -// -using System; -using Api.Data.Triggers.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Triggers.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260415151450_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Triggers.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.HasIndex("UpdatedAt"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.Triggers.Models.ExampleTrigger", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("ExampleId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Trigger") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleTrigger"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.cs b/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.cs deleted file mode 100644 index aa043be6..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.cs +++ /dev/null @@ -1,638 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Api.Data.Triggers.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - UpdatedAt = table.Column(type: "datetime(6)", nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "ExampleTrigger", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ExampleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Trigger = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_ExampleTrigger", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyClaim", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKeyRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", - column: x => x.ApiKeyId, - principalTable: "__EFIdentityApiKey", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityKey", - table: "__EFAudit", - column: "EntityKey"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityState", - table: "__EFAudit", - column: "EntityState"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", - table: "__EFIdentityApiKeyClaim", - columns: new[] { "ApiKeyId", "ClaimType" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKeyRole_RoleId", - table: "__EFIdentityApiKeyRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", - table: "__EFIdentityApiKeyRole", - columns: new[] { "ApiKeyId", "RoleId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - - migrationBuilder.CreateIndex( - name: "IX_Example_UpdatedAt", - table: "Example", - column: "UpdatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleTrigger_CreatedAt", - table: "ExampleTrigger", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_ExampleTrigger_IsDeleted", - table: "ExampleTrigger", - column: "IsDeleted"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKeyRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "ExampleTrigger"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Api.Data.Triggers/Api.Data.Triggers/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Triggers/Api.Data.Triggers/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index bef3c9d5..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,686 +0,0 @@ -// -using System; -using Api.Data.Triggers.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Api.Data.Triggers.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Api.Data.Triggers.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.HasIndex("UpdatedAt"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Api.Data.Triggers.Models.ExampleTrigger", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("ExampleId") - .HasColumnType("char(36)"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Trigger") - .IsRequired() - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.ToTable("ExampleTrigger"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityKey") - .HasColumnType("char(36)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityState") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasDefaultValue(0); - - b.Property("EntityTypeName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityKey"); - - b.HasIndex("EntityState"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("ClaimType") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.HasIndex("ApiKeyId", "ClaimType") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); - - b.ToTable("__EFIdentityApiKeyClaim", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ApiKeyId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.HasIndex("ApiKeyId", "RoleId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); - - b.ToTable("__EFIdentityApiKeyRole", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") - .WithMany() - .HasForeignKey("ApiKeyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("ApiKey"); - - b.Navigation("Role"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Api.Data.Triggers/Api.Data.Triggers/Program.cs b/Api.Data.Triggers/Api.Data.Triggers/Program.cs deleted file mode 100644 index 80733e67..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Api.Data.Triggers.Data; -using Nano.App.Api; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); diff --git a/Api.Data.Triggers/Api.Data.Triggers/Properties/InternalsVisibleTo.cs b/Api.Data.Triggers/Api.Data.Triggers/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 247e40b4..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Data.Triggers")] \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json deleted file mode 100644 index 34379ce9..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "StartupAction": "Migrate", - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Production.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Staging.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.json deleted file mode 100644 index d43a066a..00000000 --- a/Api.Data.Triggers/Api.Data.Triggers/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Data": { - "ConnectionString": null - } -} \ No newline at end of file diff --git a/Api.Data.Triggers/Dockerfile b/Api.Data.Triggers/Dockerfile deleted file mode 100644 index 6f4ffa68..00000000 --- a/Api.Data.Triggers/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Data.Triggers.dll"] \ No newline at end of file diff --git a/Api.Data.Triggers/LICENSE b/Api.Data.Triggers/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Data.Triggers/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Triggers/README.md b/Api.Data.Triggers/README.md deleted file mode 100644 index 83ec1a31..00000000 --- a/Api.Data.Triggers/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Api.Data.Triggers - -> _Nano API application with data triggers._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to -demonstrate repository autosave. Entity controllers have been simplified to showcase triggers; full controllers are unnecessary. - -Triggers for `OnInserting`, `OnInserted`, `OnUpdating`, `OnUpdated`, `OnDeleting`, and `OnDeleted` have been configured in mappings for the `Example` entity model. Whenever -the `Example` entity is **added** or **updated**, the `Example.UpdatedAt` property is automatically set to `UtcNow`. Additionally, for each trigger execution, an -`ExampleTrigger` entity is created and stored. This serves as a record demonstrating that the trigger was invoked. - -> 📖 Learn more about **[Nano Data Triggers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#triggers)**. diff --git a/Api.Data.Triggers/icon.png b/Api.Data.Triggers/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Documentation.Nonce/.docker/docker-compose.yml b/Api.Documentation.Nonce/.docker/docker-compose.yml deleted file mode 100644 index d63e7ade..00000000 --- a/Api.Documentation.Nonce/.docker/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -services: - api.documentation.nonce: - image: api.documentation.nonce - hostname: api-documentation.nonce - restart: on-failure - ports: - - 8080:8080 - - 4443:4443 - build: - context: ../Api.Documentation.Nonce - dockerfile: "Dockerfile.Local" - volumes: - - ../:/root/.dotnet/https - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Documentation.Nonce/.dockerignore b/Api.Documentation.Nonce/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Documentation.Nonce/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Documentation.Nonce/.github/config/slack.yml b/Api.Documentation.Nonce/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Documentation.Nonce/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Documentation.Nonce/.github/workflows/build-and-deploy.yml b/Api.Documentation.Nonce/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 420e282f..00000000 --- a/Api.Documentation.Nonce/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,189 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Documentation.Nonce - IMAGE_NAME: api.documentation.nonce - SERVICE_NAME: api-documentation-nonce - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - CERTIFICATE_ISSUER: letsencrypt-prod - CERTIFICATE_ORGANIZATION: ${{ vars.CERTIFICATE_ORGANIZATION }} - CERTIFICATE_HOST: ${{ github.ref == 'refs/heads/master' && vars.HOST_API_SUBDOMAIN + '.' + vars.PRODUCTION_HOST || vars.HOST_API_SUBDOMAIN + '.' + vars.STAGING_HOST }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - NONCE_TOKEN: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_NONCE_TOKEN || secrets.STAGING_NONCE_TOKEN }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/certificate.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/certificate.tmp.yaml; - sudo kubectl apply -f .kubernetes/certificate.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/ingress.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/ingress.tmp.yaml; - sudo kubectl apply -f .kubernetes/ingress.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Documentation.Nonce/.gitignore b/Api.Documentation.Nonce/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Documentation.Nonce/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Documentation.Nonce/.kubernetes/autoscaler.yaml b/Api.Documentation.Nonce/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Documentation.Nonce/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Documentation.Nonce/.kubernetes/certificate.yaml b/Api.Documentation.Nonce/.kubernetes/certificate.yaml deleted file mode 100644 index a37bdadc..00000000 --- a/Api.Documentation.Nonce/.kubernetes/certificate.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: %SERVICE_NAME%-nginx-tls - namespace: %KUBERNETES_NAMESPACE% -spec: - secretName: %CERTIFICATE_HOST%-tls - duration: 2160h - renewBefore: 720h - subject: - organizations: - - %CERTIFICATE_ORGANIZATION% - dnsNames: - - %CERTIFICATE_HOST% - privateKey: - rotationPolicy: Always - issuerRef: - name: %CERTIFICATE_ISSUER% - kind: ClusterIssuer diff --git a/Api.Documentation.Nonce/.kubernetes/configmap.yaml b/Api.Documentation.Nonce/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Documentation.Nonce/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Documentation.Nonce/.kubernetes/deployment.yaml b/Api.Documentation.Nonce/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Documentation.Nonce/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Documentation.Nonce/.kubernetes/ingress.yaml b/Api.Documentation.Nonce/.kubernetes/ingress.yaml deleted file mode 100644 index ede6835a..00000000 --- a/Api.Documentation.Nonce/.kubernetes/ingress.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ingress-%SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - annotations: - nginx.ingress.kubernetes.io/configuration-snippet: | - more_set_headers "Content-Security-Policy: script-src 'self' 'nonce-${request_id}'; style-src 'self' 'nonce-${request_id}'"; - sub_filter_once off; - sub_filter '%NONCE_TOKEN%' $request_id; - sub_filter '(]*>)(.*?)%NONCE_TOKEN%(.*?<\/body>)' '$1$2"$request_id"$3'; -spec: - ingressClassName: nginx - tls: - - hosts: - - %CERTIFICATE_HOST% - secretName: %CERTIFICATE_HOST%-tls - rules: - - host: %CERTIFICATE_HOST% - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: %SERVICE_NAME% - port: - number: 8080 diff --git a/Api.Documentation.Nonce/.kubernetes/service.yaml b/Api.Documentation.Nonce/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Documentation.Nonce/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Properties/DoNotParallelize.cs b/Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Tests.Api.Documentation.Nonce.csproj b/Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Tests.Api.Documentation.Nonce.csproj deleted file mode 100644 index eff632ef..00000000 --- a/Api.Documentation.Nonce/.tests/Tests.Api.Documentation.Nonce/Tests.Api.Documentation.Nonce.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce.Models/Api.Documentation.Nonce.Models.csproj b/Api.Documentation.Nonce/Api.Documentation.Nonce.Models/Api.Documentation.Nonce.Models.csproj deleted file mode 100644 index 7a4d41f3..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce.Models/Api.Documentation.Nonce.Models.csproj +++ /dev/null @@ -1,74 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce.sln b/Api.Documentation.Nonce/Api.Documentation.Nonce.sln deleted file mode 100644 index a7554d11..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce.sln +++ /dev/null @@ -1,136 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - localhost.pfx = localhost.pfx - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\certificate.yaml = .kubernetes\certificate.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\ingress.yaml = .kubernetes\ingress.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Documentation.Nonce.Models", "Api.Documentation.Nonce.Models\Api.Documentation.Nonce.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Documentation.Nonce", "Api.Documentation.Nonce\Api.Documentation.Nonce.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Documentation.Nonce", ".tests\Tests.Api.Documentation.Nonce\Tests.Api.Documentation.Nonce.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/Api.Documentation.Nonce.csproj b/Api.Documentation.Nonce/Api.Documentation.Nonce/Api.Documentation.Nonce.csproj deleted file mode 100644 index 536fc1e7..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/Api.Documentation.Nonce.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/Controllers/ExamplesController.cs b/Api.Documentation.Nonce/Api.Documentation.Nonce/Controllers/ExamplesController.cs deleted file mode 100644 index 24f1730a..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Documentation.Nonce.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Documentation Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("documentation")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task DocumentationAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("documentation"); - } -} \ No newline at end of file diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/Dockerfile.Local b/Api.Documentation.Nonce/Api.Documentation.Nonce/Dockerfile.Local deleted file mode 100644 index a42dbcd8..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 4443 - -ENTRYPOINT ["dotnet", "Api.Documentation.Nonce.dll"] \ No newline at end of file diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/Program.cs b/Api.Documentation.Nonce/Api.Documentation.Nonce/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/Properties/InternalsVisibleTo.cs b/Api.Documentation.Nonce/Api.Documentation.Nonce/Properties/InternalsVisibleTo.cs deleted file mode 100644 index ca10fdb2..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Documentation.Nonce")] \ No newline at end of file diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Development.json b/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Development.json deleted file mode 100644 index 23bfb871..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Development.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "App": { - "Hosting": { - "http": { - "UseHttpsRedirection": true - }, - "Https": { - "Ports": [ - 4443 - ], - "Certificate": { - "Path": "/root/.dotnet/https/localhost.pfx", - "Password": "password" - }, - "UseHttpsRequired": true - } - } - } -} \ No newline at end of file diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Production.json b/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Staging.json b/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.json b/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.json deleted file mode 100644 index eafada5a..00000000 --- a/Api.Documentation.Nonce/Api.Documentation.Nonce/appsettings.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Documentation": { - "Name": "Application", - "Description": "This is an example application", - "TermsOfService": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", - "Contact": { - "Name": "Nano Contributors", - "Email": "email@email.com", - "Url": "https://github.com/Nano-Core" - }, - "License": { - "Name": "MIT", - "Identifier": "MIT", - "Url": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE" - }, - "CspNonce": "927ba207fece4379bbd32f7d3ad55675", - "HideDefaultVersion": true - }, - "HttpPolicyHeaders": { - "Csp": { - "Scripts": { - "IsSelf": true, - "Nonces": [ - "927ba207fece4379bbd32f7d3ad55675" - ] - }, - "Styles": { - "IsSelf": true, - "Nonces": [ - "927ba207fece4379bbd32f7d3ad55675" - ] - } - } - } - } -} \ No newline at end of file diff --git a/Api.Documentation.Nonce/Dockerfile b/Api.Documentation.Nonce/Dockerfile deleted file mode 100644 index c640d06a..00000000 --- a/Api.Documentation.Nonce/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Documentation.Nonce.dll"] \ No newline at end of file diff --git a/Api.Documentation.Nonce/LICENSE b/Api.Documentation.Nonce/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Documentation.Nonce/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Documentation.Nonce/README.md b/Api.Documentation.Nonce/README.md deleted file mode 100644 index f81dd555..00000000 --- a/Api.Documentation.Nonce/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Api.Documentation.Nonce - -> _Nano API application with api documentation and nonce._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#gitHub-actions) - -## Summary -This application builds on **[Api.Hosting.Https](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Hosting.Https)**. - -This example shows using API documentation with CSP security using nonce. Run the solution and open -[https://localhost:4443/docs](https://localhost:4443/docs) in your browser to view the API documentation. - -Also a CSP nonce has been added and the policy configured to allow scripts and styles only with that nonce. The nonce value is available in `Documentation.Nonce`, -ensuring that Swagger scripts and styles are permitted. If you change `Documentation.Nonce`, the Swagger page will break with CSP script and style errors in the browser. -You can also remove the `HttpPolicyHeaders` from `appsettings` and set `Documentation.Nonce` to `null` to disable nonce enforcement. - -| Endpoint | Description | -| -------------------------------------------------- | -------------------- | -| `http://localhost:8080/api/examples/documentation` | Returns a `200 OK`. | - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -## Configuration -```json -"App": { - "Documentation": { - "Name": "Application", - "Description": "This is an example application", - "TermsOfService": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", - "Contact": { - "Name": "Nano Contributors", - "Email": "email@email.com", - "Url": "https://github.com/Nano-Core" - }, - "License": { - "Name": "MIT", - "Identifier": "MIT", - "Url": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE" - }, - "CspNonce": "927ba207fece4379bbd32f7d3ad55675", - "HideDefaultVersion": true - } -} -``` - -## Kubernetes -Annotations are configured below to automatically replace the static CSP nonce with a dynamically generated token, -ensuring that scripts and styles comply with the Content Security Policy. - -``` -kind: Ingress -metadata: - annotations: - nginx.ingress.kubernetes.io/configuration-snippet: | - more_set_headers "Content-Security-Policy: script-src 'self' 'nonce-${request_id}'; style-src 'self' 'nonce-${request_id}'"; - sub_filter_once off; - sub_filter '%NONCE_TOKEN%' $request_id; - sub_filter '(]*>)(.*?)%NONCE_TOKEN%(.*?<\/body>)' '$1$2"$request_id"$3'; -``` - -## GitHub Actions -Additional environment variables have been added to `build-and-deploy.yml` to support the new Kubernetes resources: - -```yaml -env: - NONCE_TOKEN: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_NONCE_TOKEN || secrets.STAGING_NONCE_TOKEN }} -``` diff --git a/Api.Documentation.Nonce/icon.png b/Api.Documentation.Nonce/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 QtUG7zSmhZ)eg(jP05{mZ`~Uy| diff --git a/Api.Documentation/.docker/docker-compose.dcproj b/Api.Documentation/.docker/docker-compose.dcproj deleted file mode 100644 index d9e8e500..00000000 --- a/Api.Documentation/.docker/docker-compose.dcproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Documentation/.docker/docker-compose.yml b/Api.Documentation/.docker/docker-compose.yml deleted file mode 100644 index 80173a06..00000000 --- a/Api.Documentation/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.documentation: - image: api.documentation - hostname: api-documentation - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Documentation - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Documentation/.dockerignore b/Api.Documentation/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Documentation/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Documentation/.github/config/slack.yml b/Api.Documentation/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Documentation/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Documentation/.github/workflows/build-and-deploy.yml b/Api.Documentation/.github/workflows/build-and-deploy.yml deleted file mode 100644 index a4603f6c..00000000 --- a/Api.Documentation/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Documentation - IMAGE_NAME: api.documentation - SERVICE_NAME: api-documentation - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Documentation/.gitignore b/Api.Documentation/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Documentation/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Documentation/.kubernetes/autoscaler.yaml b/Api.Documentation/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Documentation/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Documentation/.kubernetes/configmap.yaml b/Api.Documentation/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Documentation/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Documentation/.kubernetes/deployment.yaml b/Api.Documentation/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Documentation/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Documentation/.kubernetes/service.yaml b/Api.Documentation/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Documentation/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Documentation/.tests/Tests.Api.Documentation/Properties/DoNotParallelize.cs b/Api.Documentation/.tests/Tests.Api.Documentation/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Documentation/.tests/Tests.Api.Documentation/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Documentation/.tests/Tests.Api.Documentation/Tests.Api.Documentation.csproj b/Api.Documentation/.tests/Tests.Api.Documentation/Tests.Api.Documentation.csproj deleted file mode 100644 index f6d76b00..00000000 --- a/Api.Documentation/.tests/Tests.Api.Documentation/Tests.Api.Documentation.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj b/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation.sln b/Api.Documentation/Api.Documentation.sln deleted file mode 100644 index 5317a173..00000000 --- a/Api.Documentation/Api.Documentation.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Documentation.Models", "Api.Documentation.Models\Api.Documentation.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Documentation", "Api.Documentation\Api.Documentation.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Documentation", ".tests\Tests.Api.Documentation\Tests.Api.Documentation.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Documentation/Api.Documentation/Api.Documentation.csproj b/Api.Documentation/Api.Documentation/Api.Documentation.csproj deleted file mode 100644 index b0758e7f..00000000 --- a/Api.Documentation/Api.Documentation/Api.Documentation.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/Controllers/ExamplesController.cs b/Api.Documentation/Api.Documentation/Controllers/ExamplesController.cs deleted file mode 100644 index fe61e9d2..00000000 --- a/Api.Documentation/Api.Documentation/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Documentation.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Documentation Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("documentation")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task DocumentationAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("documentation"); - } -} \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/Dockerfile.Local b/Api.Documentation/Api.Documentation/Dockerfile.Local deleted file mode 100644 index 8304fa2a..00000000 --- a/Api.Documentation/Api.Documentation/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Documentation.dll"] \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/Program.cs b/Api.Documentation/Api.Documentation/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Documentation/Api.Documentation/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Documentation/Api.Documentation/Properties/InternalsVisibleTo.cs b/Api.Documentation/Api.Documentation/Properties/InternalsVisibleTo.cs deleted file mode 100644 index d56f2a5b..00000000 --- a/Api.Documentation/Api.Documentation/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Documentation")] \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/appsettings.Development.json b/Api.Documentation/Api.Documentation/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Documentation/Api.Documentation/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/appsettings.Production.json b/Api.Documentation/Api.Documentation/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Documentation/Api.Documentation/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/appsettings.Staging.json b/Api.Documentation/Api.Documentation/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Documentation/Api.Documentation/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/appsettings.json b/Api.Documentation/Api.Documentation/appsettings.json deleted file mode 100644 index 135418df..00000000 --- a/Api.Documentation/Api.Documentation/appsettings.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Documentation": { - "Name": "Application", - "Description": "This is an example application", - "TermsOfService": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", - "Contact": { - "Name": "Nano Contributors", - "Email": "email@email.com", - "Url": "https://github.com/Nano-Core" - }, - "License": { - "Name": "MIT", - "Identifier": "MIT", - "Url": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE" - }, - "CspNonce": null, - "HideDefaultVersion": true - } - } -} \ No newline at end of file diff --git a/Api.Documentation/Dockerfile b/Api.Documentation/Dockerfile deleted file mode 100644 index bc41ebbf..00000000 --- a/Api.Documentation/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Documentation.dll"] \ No newline at end of file diff --git a/Api.Documentation/LICENSE b/Api.Documentation/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Documentation/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Documentation/README.md b/Api.Documentation/README.md deleted file mode 100644 index 66faf75d..00000000 --- a/Api.Documentation/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Api.Documentation - -> _Nano API application with api documentation._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#gitHub-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example shows using API documentation for an Nano application. Run the solution and open -[http://localhost:8080/docs](http://localhost:8080/docs) in your browser to view the API documentation. - -You can experiment with the `HideDefaultVersion` setting. When set to `false`, Swagger displays both the non-versioned route and the versioned route -for the same example endpoint. When set to `true`, Swagger only displays the non-versioned route corresponding to the application's default version. - -| Endpoint | Description | -| -------------------------------------------------- | -------------------- | -| `http://localhost:8080/api/examples/documentation` | Returns a `200 OK`. | - -> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#documentation)**. - -## Configuration -```json -"App": { - "Documentation": { - "Name": "Application", - "Description": "This is an example application", - "TermsOfService": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", - "Contact": { - "Name": "Nano Contributors", - "Email": "email@email.com", - "Url": "https://github.com/Nano-Core" - }, - "License": { - "Name": "MIT", - "Identifier": "MIT", - "Url": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE" - }, - "CspNonce": "null, - "HideDefaultVersion": true - } -} -``` - diff --git a/Api.Documentation/icon.png b/Api.Documentation/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.ErrorHandling/.docker/docker-compose.yml b/Api.ErrorHandling/.docker/docker-compose.yml deleted file mode 100644 index 7a9a44dc..00000000 --- a/Api.ErrorHandling/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.errorhandling: - image: api.errorhandling - hostname: api-errorhandling - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.ErrorHandling - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge \ No newline at end of file diff --git a/Api.ErrorHandling/.dockerignore b/Api.ErrorHandling/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.ErrorHandling/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.ErrorHandling/.github/config/slack.yml b/Api.ErrorHandling/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.ErrorHandling/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ErrorHandling/.github/workflows/build-and-deploy.yml b/Api.ErrorHandling/.github/workflows/build-and-deploy.yml deleted file mode 100644 index ef434196..00000000 --- a/Api.ErrorHandling/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.ErrorHandling - IMAGE_NAME: api.errorhandling - SERVICE_NAME: api-errorhandling - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ErrorHandling/.gitignore b/Api.ErrorHandling/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.ErrorHandling/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ErrorHandling/.kubernetes/autoscaler.yaml b/Api.ErrorHandling/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.ErrorHandling/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ErrorHandling/.kubernetes/configmap.yaml b/Api.ErrorHandling/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.ErrorHandling/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ErrorHandling/.kubernetes/deployment.yaml b/Api.ErrorHandling/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.ErrorHandling/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ErrorHandling/.kubernetes/service.yaml b/Api.ErrorHandling/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.ErrorHandling/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Properties/DoNotParallelize.cs b/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Tests.Api.ErrorHandling.csproj b/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Tests.Api.ErrorHandling.csproj deleted file mode 100644 index 83f4b412..00000000 --- a/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Tests.Api.ErrorHandling.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj b/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj deleted file mode 100644 index 890be2f1..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling.sln b/Api.ErrorHandling/Api.ErrorHandling.sln deleted file mode 100644 index b6538ddd..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling.sln +++ /dev/null @@ -1,134 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ErrorHandling.Models", "Api.ErrorHandling.Models\Api.ErrorHandling.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ErrorHandling", "Api.ErrorHandling\Api.ErrorHandling.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ErrorHandling", ".tests\Tests.Api.ErrorHandling\Tests.Api.ErrorHandling.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.ErrorHandling/Api.ErrorHandling/Api.ErrorHandling.csproj b/Api.ErrorHandling/Api.ErrorHandling/Api.ErrorHandling.csproj deleted file mode 100644 index 3bf684a5..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/Api.ErrorHandling.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/Controllers/ExamplesController.cs b/Api.ErrorHandling/Api.ErrorHandling/Controllers/ExamplesController.cs deleted file mode 100644 index 45d07121..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/Controllers/ExamplesController.cs +++ /dev/null @@ -1,235 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Exceptions; -using Nano.Data.Abstractions.Identity.Exceptions; -using Vivet.AspNetCore.RequestVirusScan.Exceptions; -using Vivet.AspNetCore.RequestVirusScan.Models.Enums; - -namespace Api.ErrorHandling.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Error. - [HttpGet] - [Route("exception")] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task ExceptionAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new Exception("error"); - } - - /// - /// Aggregate Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Request Timeout. - [HttpGet] - [Route("exception-aggregate")] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task AggregateAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new AggregateException("error-aggregate", new Exception("inner exception")); - } - - /// - /// Virus Scan Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Bad Request. - [HttpGet] - [Route("exception-virus-scan")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public virtual async Task VirusScanAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new VirusScanException(ResultType.VirusFound, "file.exe", "Dangerous Virus"); - } - - /// - /// Unauthorized Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Unauthorized. - [HttpGet] - [Route("exception-unauthorized")] - [ProducesResponseType((int)HttpStatusCode.Unauthorized)] - public virtual async Task UnauthorizedAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new UnauthorizedException(); - } - - /// - /// Permission Denied Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Forbidden. - [HttpGet] - [Route("exception-permission-denied")] - [ProducesResponseType((int)HttpStatusCode.Forbidden)] - public virtual async Task PermissionDeniedAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new PermissionDeniedException(); - } - - /// - /// Identity Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Bad Request. - [HttpGet] - [Route("exception-identity")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public virtual async Task IdentityAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new IdentityException("error"); - } - - /// - /// Bad Request Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Bad Request. - [HttpGet] - [Route("exception-bad-request")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public virtual async Task BadRequestAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new BadRequestException("error"); - } - - /// - /// Bad Request (Coded) Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Bad Request. - [HttpGet] - [Route("exception-bad-request-coded")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public virtual async Task BadRequestCodedAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new BadRequestException("error-coded", true); - } - - /// - /// Bad Request (Translated) Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Bad Request. - [HttpGet] - [Route("exception-bad-request-translated")] - [ProducesResponseType((int)HttpStatusCode.BadRequest)] - public virtual async Task BadRequestTranslatedAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new BadRequestException("error-translated", false, true); - } - - /// - /// Task Canceled Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Request Timeout. - [HttpGet] - [Route("exception-task-canceled")] - [ProducesResponseType((int)HttpStatusCode.RequestTimeout)] - public virtual async Task TaskCanceledAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new TaskCanceledException("error-task-cancelled"); - } - - /// - /// Operation Cancelled Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Request Timeout. - [HttpGet] - [Route("exception-operation-canceled")] - [ProducesResponseType((int)HttpStatusCode.RequestTimeout)] - public virtual async Task OperationCancelledAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new OperationCanceledException("error-operation-cancelled"); - } - - /// - /// Problem Details Exception Action. - /// - /// The cancellation token. - /// Nothing. - /// Loop Detected. - [HttpGet] - [Route("exception-problem-details")] - [ProducesResponseType((int)HttpStatusCode.LoopDetected)] - public virtual async Task ProblemDetailsAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new ProblemDetailsException(new ProblemDetails - { - Status = (int)HttpStatusCode.LoopDetected, - Title = "Loop Detected", - Detail = "Error" - }); - } - - /// - /// Bad Request Validation Action. - /// - /// - /// The cancellation token. - /// A message. - /// Error. - [HttpGet] - [Route("validation-error")] - [ProducesResponseType((int)HttpStatusCode.InternalServerError)] - public virtual async Task BadRequestValidationAsync([FromQuery][Required]string value, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok(); - } -} \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local b/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local deleted file mode 100644 index 77abb0b3..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ErrorHandling.dll"] \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/Program.cs b/Api.ErrorHandling/Api.ErrorHandling/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ErrorHandling/Api.ErrorHandling/Properties/InternalsVisibleTo.cs b/Api.ErrorHandling/Api.ErrorHandling/Properties/InternalsVisibleTo.cs deleted file mode 100644 index cd41041c..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ErrorHandling")] \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Development.json b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Production.json b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Staging.json b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/appsettings.json b/Api.ErrorHandling/Api.ErrorHandling/appsettings.json deleted file mode 100644 index 5c5be510..00000000 --- a/Api.ErrorHandling/Api.ErrorHandling/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "ErrorHandling": { - "ExposeErrors": true - } - } -} \ No newline at end of file diff --git a/Api.ErrorHandling/Dockerfile b/Api.ErrorHandling/Dockerfile deleted file mode 100644 index de88ee4d..00000000 --- a/Api.ErrorHandling/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.ErrorHandling.dll"] \ No newline at end of file diff --git a/Api.ErrorHandling/LICENSE b/Api.ErrorHandling/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.ErrorHandling/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.ErrorHandling/README.md b/Api.ErrorHandling/README.md deleted file mode 100644 index e52aa1cd..00000000 --- a/Api.ErrorHandling/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Api.ErrorHandling - -> _Nano API application showing error handling._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example demonstrates how Nano API error handling processes exceptions and other errors to produce the appropriate HTTP responses. - -The following endpoint is available for testing: - -| Endpoint | Description | -| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/exception` | Returns a `500 Internal Server Error` response. `Detail` exposed because `ExposeErrors` is set to `true`. | -| `http://localhost:8080/api/examples/exception-aggregate` | Returns a `500 Internal Server Error` response. `Detail` exposed because `ExposeErrors` is set to `true`. | -| `http://localhost:8080/api/examples/exception-virus-scan` | Returns a `400 Bad Request` response. `Detail` always exposed. | -| `http://localhost:8080/api/examples/exception-unauthorized` | Returns a `401 Unauthorized` response. `Detail` always exposed. | -| `http://localhost:8080/api/examples/exception-permission-denied` | Returns a `403 Forbidden` response. Usually no `Detail`, but always exposed. | -| `http://localhost:8080/api/examples/exception-identity` | Returns a `400 Bad Request` response. `Detail` always exposed. | -| `http://localhost:8080/api/examples/exception-bad-request` | Returns a `400 Bad Request` response. `Detail` always exposed. | -| `http://localhost:8080/api/examples/exception-bad-request-coded` | Returns a `400 Bad Request` response. `Detail` always exposed. `IsCoded` property added and set to `true`. | -| `http://localhost:8080/api/examples/exception-bad-request-translated` | Returns a `400 Bad Request` response. `Detail` always exposed. `IsTranslated` property added set to `true`. | -| `http://localhost:8080/api/examples/exception-task-canceled` | Returns a `408 Request Timeout` response. `Detail` always exposed. | -| `http://localhost:8080/api/examples/exception-operation-canceled` | Returns a `408 Request Timeout` response. `Detail` always exposed. | -| `http://localhost:8080/api/examples/exception-problem-details` | Varies depending on the `ProblemDetails`. | -| `http://localhost:8080/api/examples/validation-error` | Returns a `400 Bad Request` response. `Detail` always exposed. | - -Alternatively, toggle the `ExposeErrors` to `false`, and observe that messages from `500 Internal Server Errors` no longer will be exposed. - -> 📖 Learn more about **[Nano Error Handling](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#error-handling)**. - -## Configuration -```json -"App": { - "ErrorHandling": { - "ExposeErrors": true - } -} -``` diff --git a/Api.ErrorHandling/icon.png b/Api.ErrorHandling/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/.docker/docker-compose.yml b/Api.Eventing.RabbitMq/.docker/docker-compose.yml deleted file mode 100644 index de9772ea..00000000 --- a/Api.Eventing.RabbitMq/.docker/docker-compose.yml +++ /dev/null @@ -1,34 +0,0 @@ -services: - api.eventing.rabbitmq: - image: api.eventing.rabbitmq - hostname: api-eventing-rabbitmq - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Eventing.RabbitMq - dockerfile: "Dockerfile.Local" - depends_on: - - eventing - networks: - - network - - eventing: - image: rabbitmq:management - hostname: rabbitmq - ports: - - 5671:5671 - - 5672:5672 - - 15671:15671 - - 15672:15672 - networks: - - network - environment: - RABBITMQ_DEFAULT_USER: rabbitmq_user - RABBITMQ_DEFAULT_PASS: password - RABBITMQ_DEFAULT_VHOST: / - -networks: - network: - name: network - driver: bridge diff --git a/Api.Eventing.RabbitMq/.dockerignore b/Api.Eventing.RabbitMq/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Eventing.RabbitMq/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Eventing.RabbitMq/.github/config/slack.yml b/Api.Eventing.RabbitMq/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Eventing.RabbitMq/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml b/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 77ae6c24..00000000 --- a/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Eventing.RabbitMq - IMAGE_NAME: api.eventing.rabbitmq - SERVICE_NAME: api.eventing-rabbitmq - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/.gitignore b/Api.Eventing.RabbitMq/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Eventing.RabbitMq/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/.kubernetes/autoscaler.yaml b/Api.Eventing.RabbitMq/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Eventing.RabbitMq/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml b/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml b/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml deleted file mode 100644 index a1717afc..00000000 --- a/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml +++ /dev/null @@ -1,84 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Eventing__Credentials__Secret - valueFrom: - secretKeyRef: - name: rabbitmq - key: rabbitmq-password - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Eventing.RabbitMq/.kubernetes/service.yaml b/Api.Eventing.RabbitMq/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Eventing.RabbitMq/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Properties/DoNotParallelize.cs b/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Tests.Api.Eventing.RabbitMq.csproj b/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Tests.Api.Eventing.RabbitMq.csproj deleted file mode 100644 index 6bca9ffb..00000000 --- a/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Tests.Api.Eventing.RabbitMq.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj deleted file mode 100644 index 04328227..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.sln b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.sln deleted file mode 100644 index 60830083..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Eventing.RabbitMq.Models", "Api.Eventing.RabbitMq.Models\Api.Eventing.RabbitMq.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Eventing.RabbitMq", "Api.Eventing.RabbitMq\Api.Eventing.RabbitMq.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Eventing.RabbitMq", ".tests\Tests.Api.Eventing.RabbitMq\Tests.Api.Eventing.RabbitMq.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing", "..\..\Nano.Library\Nano.Eventing\Nano.Eventing.csproj", "{A8E623BC-70EA-3CC8-AFAD-797F006C4A41}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.RabbitMq", "..\..\Nano.Library\Nano.Eventing.RabbitMq\Nano.Eventing.RabbitMq.csproj", "{0789C863-B371-F968-B9C9-5F3CF3DD9897}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.Build.0 = Release|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {0789C863-B371-F968-B9C9-5F3CF3DD9897} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.csproj b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.csproj deleted file mode 100644 index a4d4f7dd..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Controllers/ExamplesController.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Controllers/ExamplesController.cs deleted file mode 100644 index cfa5a821..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Controllers/ExamplesController.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.Eventing.RabbitMq.Eventing.Models; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Eventing.Abstractions; - -namespace Api.Eventing.RabbitMq.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IEventing eventing) : BaseController(logger) -{ - /// - /// Eventing Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("eventing")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task EventingAsync(CancellationToken cancellationToken = default) - { - await eventing - .PublishAsync(new EventModel - { - Text = "Testing eventing" - }, cancellationToken: cancellationToken); - - return this.Ok("eventing"); - } - - /// - /// Eventing with Routing Key Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("eventing-routing-key")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task EventingRoutingKeyAsync(CancellationToken cancellationToken = default) - { - await eventing - .PublishAsync(new EventModelRoutingKey - { - Text = "Testing eventing" - }, "routing-key", cancellationToken); - - return this.Ok("eventing-routing-key"); - } -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local deleted file mode 100644 index e183367d..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandler.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandler.cs deleted file mode 100644 index 3d34f4e5..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Api.Eventing.RabbitMq.Eventing.Models; -using Nano.Eventing.Abstractions; - -namespace Api.Eventing.RabbitMq.Eventing; - -/// -public class EventingHandler : BaseEventHandler -{ - /// - public override async Task CallbackAsync(EventModel @event, bool isRedelivered, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - Console.WriteLine(@event.Text); - } -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandlerRoutingKey.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandlerRoutingKey.cs deleted file mode 100644 index b9b49f5b..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandlerRoutingKey.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Api.Eventing.RabbitMq.Eventing.Models; -using Nano.Eventing.Abstractions; - -namespace Api.Eventing.RabbitMq.Eventing; - -/// -public class EventingHandlerRoutingKey : BaseEventHandler -{ - /// - /// Routing Key. - /// - public static string RoutingKey => "routing-key"; - - /// - public override async Task CallbackAsync(EventModelRoutingKey @event, bool isRedelivered, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - Console.WriteLine($"Event Routed: {@event.Text}"); - } -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModel.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModel.cs deleted file mode 100644 index 6974e555..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Api.Eventing.RabbitMq.Eventing.Models; - -/// -/// Event Model. -/// -public class EventModel -{ - /// - /// Text. - /// - public virtual string? Text { get; set; } -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModelRoutingKey.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModelRoutingKey.cs deleted file mode 100644 index 8c688a0b..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModelRoutingKey.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Api.Eventing.RabbitMq.Eventing.Models; - -/// -/// Event Model Routing Key. -/// -public class EventModelRoutingKey -{ - /// - /// Text. - /// - public virtual string? Text { get; set; } -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Program.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Program.cs deleted file mode 100644 index a2213efa..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Api; -using Nano.Eventing.Extensions; -using Nano.Eventing.RabbitMq; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoEventing(); - }) - .Build() - .Run(); diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs deleted file mode 100644 index b9420e8f..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Eventing.RabbitMq")] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Development.json b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Production.json b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Staging.json b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.json b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.json deleted file mode 100644 index 8dc5251a..00000000 --- a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - } - }, - "Eventing": { - "Host": "rabbitmq", - "VHost": "/", - "Port": 5672, - "UseSsl": false, - "Timeout": "00:00:30", - "Heartbeat": 60, - "PrefetchCount": 50, - "Credentials": { - "Id": "rabbitmq_user", - "Secret": "password" - }, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } -} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Dockerfile b/Api.Eventing.RabbitMq/Dockerfile deleted file mode 100644 index 4775d14f..00000000 --- a/Api.Eventing.RabbitMq/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/LICENSE b/Api.Eventing.RabbitMq/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Eventing.RabbitMq/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/README.md b/Api.Eventing.RabbitMq/README.md deleted file mode 100644 index 537fd058..00000000 --- a/Api.Eventing.RabbitMq/README.md +++ /dev/null @@ -1,134 +0,0 @@ -# Api.Eventing.RabbitMq - -> _Nano API application with rabbitmq eventing._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -While it is uncommon for the same application to both publish and consume an event, this pattern is used here for demonstration purposes. -In real-world scenarios, one service typically publishes events while other services consume them. For simplicity, this example combines both roles in a single service. - -Run the endpoints and observe an `EventModel` instance being published and handled in the `EventingHandler`. Check the console output for -the message: `Testing eventing` or `Event Routed: Testing eventing`, depending on the endpoint. This message is written by the `EventingHandler` when the -event is received. - -You can also monitor the messages via the RabbitMQ management interface: **[http://localhost:15672](http://localhost:15672)** - -An eventing health check is configured to target the RabbitMQ. -Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#health-checks)** - -The following endpoint is available for testing: - -| Endpoint | Description | -| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/eventing` | Returns a simple `200 OK` response. Publishes a message that wiil be consumed by the `EventHandler` | -| `http://localhost:8080/api/examples/eventing-routing-key` | Returns a simple `200 OK` response. Publishes a message using routing key that wiil be consumed by the `EventHandler` | - -> 📖 Learn more about **[Nano.Eventing.RabbitMq](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Eventing.RabbitMq)**. - -## Registration -The following eventing provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoEventing(); -}) -... -``` - -## Configuration -Configured the application with the necessary eventing setup. - -```json -"Eventing": { - "Host": "rabbitmq", - "VHost": "/", - "Port": 5672, - "UseSsl": false, - "Timeout": "00:00:30", - "Heartbeat": 60, - "PrefetchCount": 50, - "Credentials": { - "Id": "rabbitmq_user", - "Secret": "password" - }, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } -} -``` - -Additionally, application health-checks have been enabled with the configuration. - -```json -"App": { - "HealthCheck": { - } -} -``` - -## Docker Compose -Added RabbitMQ as a service dependency in `docker-compose.yml`. - -```yaml -services: - api.eventing.rabbitmq: - depends_on: - - eventing - - eventing: - image: rabbitmq:management - hostname: rabbitmq - ports: - - 5671:5671 - - 5672:5672 - - 15671:15671 - - 15672:15672 - networks: - - network - environment: - RABBITMQ_DEFAULT_USER: rabbitmq_user - RABBITMQ_DEFAULT_PASS: password - RABBITMQ_DEFAULT_VHOST: / - -``` -## Kubernetes -Added the `rabbitmq` secret for password to the `deployment.yaml`. - -```json -spec: - template: - spec: - containers: - env: - - name: Eventing__Credentials__Secret - valueFrom: - secretKeyRef: - name: rabbitmq - key: rabbitmq-password -``` - -> ⚠️ The `rabbitmq` secret is created alongside the **[Nano Azure Kubernetes Eventing](https://github.com/Nano-Core/Nano.Azure.Kubernetes/tree/master/Nano.Azure.Kubernetes.RabbitMQ)** diff --git a/Api.Eventing.RabbitMq/icon.png b/Api.Eventing.RabbitMq/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.HealthChecks/.docker/docker-compose.yml b/Api.HealthChecks/.docker/docker-compose.yml deleted file mode 100644 index dc34bde1..00000000 --- a/Api.HealthChecks/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.healthchecks: - image: api.healthchecks - hostname: api-healthchecks - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.HealthChecks - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.HealthChecks/.dockerignore b/Api.HealthChecks/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.HealthChecks/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.HealthChecks/.github/config/slack.yml b/Api.HealthChecks/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.HealthChecks/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.HealthChecks/.github/workflows/build-and-deploy.yml b/Api.HealthChecks/.github/workflows/build-and-deploy.yml deleted file mode 100644 index b863d9ee..00000000 --- a/Api.HealthChecks/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,236 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.HealthChecks - IMAGE_NAME: api.healthchecks - SERVICE_NAME: api-healthchecks - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} - AVAILABILITY_URI: ${{ github.ref == 'refs/heads/master' && format('https://{0}/healthz', secrets.PRODUCTION_HOST) || format('https://{0}/healthz', secrets.STAGING_HOST) }} - AVAILABILITY_CHECK_FREQUENCY: 300 -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Add Availability Check - id: add-availability-check - shell: pwsh - run: | - sudo az extension add -n application-insights; - - $env:SERVICE_NAME_INSIGTHS = $env:SERVICE_NAME + "-insights"; - $env:APPLICATION_INSIGHT_ID = sudo az monitor app-insights component show --query "[?contains(name, '$env:SERVICE_NAME_INSIGTHS')].[id]" -o tsv; - - if ([string]::IsNullOrEmpty($env:APPLICATION_INSIGHT_ID)) - { - $env:WORKSPACE_ID = sudo az monitor log-analytics workspace list --query "[?contains(name, 'log-analytics')].[id]" -o tsv; - - if (-not [string]::IsNullOrEmpty($env:WORKSPACE_ID)) - { - $env:APPLICATION_INSIGHT_ID = sudo az monitor app-insights component create ` - -a $env:SERVICE_NAME_INSIGTHS ` - -l $env:AZURE_LOCATION ` - -g $env:AZURE_GROUP ` - --workspace $env:WORKSPACE_ID ` - --query "[id]" -o tsv; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - } - }; - - $env:SERVICE_NAME_AVAILABILITY = $env:SERVICE_NAME + '-availability-' + $env:ASPNETCORE_ENVIRONMENT.ToLower(); - $env:AVAILABILITY_ID = sudo az monitor app-insights web-test list -g $env:AZURE_GROUP --query "[?contains(name, '$env:SERVICE_NAME_AVAILABILITY')].[id]" -o tsv; - - if ([string]::IsNullOrEmpty($env:AVAILABILITY_ID)) - { - $env:APPLICATION_INSIGHT_HIDDEN_LINK = 'hidden-link:' + $env:APPLICATION_INSIGHT_ID + '=Resource'; - sudo az monitor app-insights web-test create ` - -n $env:SERVICE_NAME_AVAILABILITY ` - --defined-web-test-name $env:SERVICE_NAME_AVAILABILITY ` - -g $env:AZURE_GROUP ` - -l $env:AZURE_LOCATION ` - --kind multistep ` - --web-test-kind standard ` - --frequency $env:AVAILABILITY_CHECK_FREQUENCY ` - --enabled true ` - --retry-enabled true ` - --ssl-check true ` - --ssl-lifetime-check 30 ` - --http-verb GET ` - --request-url $env:AVAILABILITY_URI ` - --expected-status-code 200 ` - --content-validation content-match='\"status\":\"unhealthy\"' ignore-case=true pass-if-text-found=false ` - --tags $env:APPLICATION_INSIGHT_HIDDEN_LINK ` - --locations Id='us-ca-sjc-azr' ` - --locations Id='us-va-ash-azr' ` - --locations Id='emea-gb-db3-azr' ` - --locations Id='emea-nl-ams-azr' ` - --locations Id='apac-hk-hkn-azr'; - }; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.HealthChecks/.gitignore b/Api.HealthChecks/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.HealthChecks/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.HealthChecks/.kubernetes/autoscaler.yaml b/Api.HealthChecks/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.HealthChecks/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.HealthChecks/.kubernetes/configmap.yaml b/Api.HealthChecks/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.HealthChecks/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.HealthChecks/.kubernetes/deployment.yaml b/Api.HealthChecks/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.HealthChecks/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.HealthChecks/.kubernetes/service.yaml b/Api.HealthChecks/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.HealthChecks/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Properties/DoNotParallelize.cs b/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Tests.Api.HealthChecks.csproj b/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Tests.Api.HealthChecks.csproj deleted file mode 100644 index 5b02dede..00000000 --- a/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Tests.Api.HealthChecks.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj b/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks.sln b/Api.HealthChecks/Api.HealthChecks.sln deleted file mode 100644 index 50d5122c..00000000 --- a/Api.HealthChecks/Api.HealthChecks.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.HealthChecks.Models", "Api.HealthChecks.Models\Api.HealthChecks.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.HealthChecks", "Api.HealthChecks\Api.HealthChecks.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.HealthChecks", ".tests\Tests.Api.HealthChecks\Tests.Api.HealthChecks.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.HealthChecks/Api.HealthChecks/Api.HealthChecks.csproj b/Api.HealthChecks/Api.HealthChecks/Api.HealthChecks.csproj deleted file mode 100644 index 83ddff6a..00000000 --- a/Api.HealthChecks/Api.HealthChecks/Api.HealthChecks.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/Controllers/ExamplesController.cs b/Api.HealthChecks/Api.HealthChecks/Controllers/ExamplesController.cs deleted file mode 100644 index b491a708..00000000 --- a/Api.HealthChecks/Api.HealthChecks/Controllers/ExamplesController.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Diagnostics.HealthChecks; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.HealthChecks.Controllers; - -/// -/// Controller with examples. -/// -/// The -/// The . -public class ExamplesController(ILogger logger, HealthCheckService healthCheckService) - : BaseController(logger) -{ - private readonly HealthCheckService healthCheckService = healthCheckService ?? throw new ArgumentNullException(nameof(healthCheckService)); - - /// - /// Health Check Action. - /// - /// The cancellation token. - /// A message. - /// Success. - /// Service Unavailable. - [HttpGet] - [Route("health-check")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task HealthCheckAsync(CancellationToken cancellationToken = default) - { - var report = await this.healthCheckService - .CheckHealthAsync(cancellationToken); - - var status = report.Status == HealthStatus.Healthy - ? (int)HttpStatusCode.OK - : (int)HttpStatusCode.ServiceUnavailable; - - return this.StatusCode(status, new - { - status = report.Status.ToString(), - checks = report.Entries - .Select(x => new - { - name = x.Key, - status = x.Value.Status.ToString(), - description = x.Value.Description - }) - }); - } - - /// - /// Webhook Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpPost] - [Route("webhook")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task WebhookAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok(); - } -} \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local b/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local deleted file mode 100644 index 927b61e9..00000000 --- a/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.HealthChecks.dll"] \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/Program.cs b/Api.HealthChecks/Api.HealthChecks/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.HealthChecks/Api.HealthChecks/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.HealthChecks/Api.HealthChecks/Properties/InternalsVisibleTo.cs b/Api.HealthChecks/Api.HealthChecks/Properties/InternalsVisibleTo.cs deleted file mode 100644 index a92131f4..00000000 --- a/Api.HealthChecks/Api.HealthChecks/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.HealthChecks")] \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json b/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.Production.json b/Api.HealthChecks/Api.HealthChecks/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.HealthChecks/Api.HealthChecks/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.Staging.json b/Api.HealthChecks/Api.HealthChecks/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.HealthChecks/Api.HealthChecks/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.json b/Api.HealthChecks/Api.HealthChecks/appsettings.json deleted file mode 100644 index 053b76d1..00000000 --- a/Api.HealthChecks/Api.HealthChecks/appsettings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - } - } -} \ No newline at end of file diff --git a/Api.HealthChecks/Dockerfile b/Api.HealthChecks/Dockerfile deleted file mode 100644 index 2e170ee4..00000000 --- a/Api.HealthChecks/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.HealthChecks.dll"] \ No newline at end of file diff --git a/Api.HealthChecks/LICENSE b/Api.HealthChecks/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.HealthChecks/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.HealthChecks/README.md b/Api.HealthChecks/README.md deleted file mode 100644 index 2b65dbd6..00000000 --- a/Api.HealthChecks/README.md +++ /dev/null @@ -1,125 +0,0 @@ -# Api.HealthChecks - -> _Nano API application with health checks._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [GitHub Actions](#gitHub-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example illustrates the use of Nano API health-checks. - -Open [http://localhost:8080/healthz](http://localhost:8080/healthz) to view the startup health-check JSON report. - -A webhook is configured for health-check that will trigger if the application becomes unhealthy. - -| Endpoint | Description | -| ------------------------------------------------- | ------------------------------------------------------ | -| `http://localhost:8080/api/examples/health-check` | Returns a `200 OK` response with health-check status. | -| `http://localhost:8080/api/examples/webhook` | Returns a `200 OK` response. | - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Apihealth-checks)**. - -## Configuration -There is no configuration for HealtCheck, the section has just been added to enable the feature. - -```json -"App": { - "HealthCheck": { - } -} -``` - -## GitHub Actions -Optionally, you can configure an availability check in Azure using Application Insights to continuously monitor your application's health and responsiveness. - -> ⚠️ This requires the application to be exposed publicly from Kubernetes via an `ingress` configuration. - -Add the following environment variables. - -```yaml -env: - AVAILABILITY_URI: ${{ github.ref == 'refs/heads/master' && format('https://{0}/healthz', secrets.PRODUCTION_HOST) || format('https://{0}/healthz', secrets.STAGING_HOST) }} - AVAILABILITY_CHECK_FREQUENCY: 300 -``` - -...and then add the `Add Availability Check` step to the action pipeline. - -```yaml - - name: Add Availability Check - id: add-availability-check - shell: pwsh - run: | - sudo az extension add -n application-insights; - - $env:SERVICE_NAME_INSIGTHS = $env:SERVICE_NAME + "-insights"; - $env:APPLICATION_INSIGHT_ID = sudo az monitor app-insights component show --query "[?contains(name, '$env:SERVICE_NAME_INSIGTHS')].[id]" -o tsv; - - if ([string]::IsNullOrEmpty($env:APPLICATION_INSIGHT_ID)) - { - $env:WORKSPACE_ID = sudo az monitor log-analytics workspace list --query "[?contains(name, 'log-analytics')].[id]" -o tsv; - - if (-not [string]::IsNullOrEmpty($env:WORKSPACE_ID)) - { - $env:APPLICATION_INSIGHT_ID = sudo az monitor app-insights component create ` - -a $env:SERVICE_NAME_INSIGTHS ` - -l $env:AZURE_LOCATION ` - -g $env:AZURE_GROUP ` - --workspace $env:WORKSPACE_ID ` - --query "[id]" -o tsv; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - } - }; - - $env:SERVICE_NAME_AVAILABILITY = $env:SERVICE_NAME + '-availability-' + $env:ASPNETCORE_ENVIRONMENT.ToLower(); - $env:AVAILABILITY_ID = sudo az monitor app-insights web-test list -g $env:AZURE_GROUP --query "[?contains(name, '$env:SERVICE_NAME_AVAILABILITY')].[id]" -o tsv; - - if ([string]::IsNullOrEmpty($env:AVAILABILITY_ID)) - { - $env:APPLICATION_INSIGHT_HIDDEN_LINK = 'hidden-link:' + $env:APPLICATION_INSIGHT_ID + '=Resource'; - sudo az monitor app-insights web-test create ` - -n $env:SERVICE_NAME_AVAILABILITY ` - --defined-web-test-name $env:SERVICE_NAME_AVAILABILITY ` - -g $env:AZURE_GROUP ` - -l $env:AZURE_LOCATION ` - --kind multistep ` - --web-test-kind standard ` - --frequency $env:AVAILABILITY_CHECK_FREQUENCY ` - --enabled true ` - --retry-enabled true ` - --ssl-check true ` - --ssl-lifetime-check 30 ` - --http-verb GET ` - --request-url $env:AVAILABILITY_URI ` - --expected-status-code 200 ` - --content-validation content-match='\"status\":\"unhealthy\"' ignore-case=true pass-if-text-found=false ` - --tags $env:APPLICATION_INSIGHT_HIDDEN_LINK ` - --locations Id='us-ca-sjc-azr' ` - --locations Id='us-va-ash-azr' ` - --locations Id='emea-gb-db3-azr' ` - --locations Id='emea-nl-ams-azr' ` - --locations Id='apac-hk-hkn-azr'; - }; - if ($LastExitCode -ne 0) - { - throw "error"; - }; -``` diff --git a/Api.HealthChecks/icon.png b/Api.HealthChecks/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Hosting.Http/.docker/docker-compose.yml b/Api.Hosting.Http/.docker/docker-compose.yml deleted file mode 100644 index 04d84d49..00000000 --- a/Api.Hosting.Http/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.hosting.http: - image: api.hosting.http - hostname: api-hosting-http - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Hosting.Http - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Hosting.Http/.dockerignore b/Api.Hosting.Http/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Hosting.Http/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Hosting.Http/.github/config/slack.yml b/Api.Hosting.Http/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Hosting.Http/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Hosting.Http/.github/workflows/build-and-deploy.yml b/Api.Hosting.Http/.github/workflows/build-and-deploy.yml deleted file mode 100644 index a944f6cf..00000000 --- a/Api.Hosting.Http/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Hosting.Http - IMAGE_NAME: api.hosting.http - SERVICE_NAME: api-hosting-http - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Hosting.Http/.gitignore b/Api.Hosting.Http/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Hosting.Http/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Hosting.Http/.kubernetes/autoscaler.yaml b/Api.Hosting.Http/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Hosting.Http/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Hosting.Http/.kubernetes/configmap.yaml b/Api.Hosting.Http/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Hosting.Http/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Hosting.Http/.kubernetes/deployment.yaml b/Api.Hosting.Http/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Hosting.Http/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Hosting.Http/.kubernetes/service.yaml b/Api.Hosting.Http/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Hosting.Http/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Properties/DoNotParallelize.cs b/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Tests.Api.Hosting.Http.csproj b/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Tests.Api.Hosting.Http.csproj deleted file mode 100644 index 1777aecb..00000000 --- a/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Tests.Api.Hosting.Http.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj b/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http.sln b/Api.Hosting.Http/Api.Hosting.Http.sln deleted file mode 100644 index a595b1ed..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.Http.Models", "Api.Hosting.Http.Models\Api.Hosting.Http.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.Http", "Api.Hosting.Http\Api.Hosting.Http.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Hosting.Http", ".tests\Tests.Api.Hosting.Http\Tests.Api.Hosting.Http.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Hosting.Http/Api.Hosting.Http/Api.Hosting.Http.csproj b/Api.Hosting.Http/Api.Hosting.Http/Api.Hosting.Http.csproj deleted file mode 100644 index cd0c7fed..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/Api.Hosting.Http.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/Controllers/ExamplesController.cs b/Api.Hosting.Http/Api.Hosting.Http/Controllers/ExamplesController.cs deleted file mode 100644 index 8bc2c176..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Hosting.Http.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Http Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("http")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task HttpAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("http"); - } -} \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local b/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local deleted file mode 100644 index 272c1ffb..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Hosting.Http.dll"] \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/Program.cs b/Api.Hosting.Http/Api.Hosting.Http/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Hosting.Http/Api.Hosting.Http/Properties/InternalsVisibleTo.cs b/Api.Hosting.Http/Api.Hosting.Http/Properties/InternalsVisibleTo.cs deleted file mode 100644 index e5d9e205..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Hosting.Http")] \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Development.json b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Production.json b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Staging.json b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/appsettings.json b/Api.Hosting.Http/Api.Hosting.Http/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.Hosting.Http/Api.Hosting.Http/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.Hosting.Http/Dockerfile b/Api.Hosting.Http/Dockerfile deleted file mode 100644 index 2d614ac6..00000000 --- a/Api.Hosting.Http/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Hosting.Http.dll"] \ No newline at end of file diff --git a/Api.Hosting.Http/LICENSE b/Api.Hosting.Http/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Hosting.Http/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Hosting.Http/README.md b/Api.Hosting.Http/README.md deleted file mode 100644 index a3157f70..00000000 --- a/Api.Hosting.Http/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Api.Hosting.Http - -> _Nano API application with http._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. The rest of the setup remains largely unchanged. - -This example simply shows configuring a Nano application for HTTP exposure. - -The following endpoint is available for testing: - -| Endpoint | Description | -| ------------------------------------------ | -------------------------------------- | -| `http://localhost:8080/api/examples/http` | Returns a simple `200 OK` response. | - -> 📖 Learn more about **[Nano Hosting Http](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#http)**. diff --git a/Api.Hosting.Http/icon.png b/Api.Hosting.Http/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Hosting.Https/.docker/docker-compose.yml b/Api.Hosting.Https/.docker/docker-compose.yml deleted file mode 100644 index 62a4f392..00000000 --- a/Api.Hosting.Https/.docker/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -services: - api.hosting.https: - image: api.hosting.https - hostname: api-hosting-https - restart: on-failure - ports: - - 8080:8080 - - 4443:4443 - build: - context: ../Api.Hosting.Https - dockerfile: "Dockerfile.Local" - volumes: - - ../:/root/.dotnet/https - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Hosting.Https/.dockerignore b/Api.Hosting.Https/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Hosting.Https/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Hosting.Https/.github/config/slack.yml b/Api.Hosting.Https/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Hosting.Https/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Hosting.Https/.github/workflows/build-and-deploy.yml b/Api.Hosting.Https/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 837b9252..00000000 --- a/Api.Hosting.Https/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,188 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Hosting.Https - IMAGE_NAME: api.hosting.https - SERVICE_NAME: api-hosting-https - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - CERTIFICATE_ISSUER: letsencrypt-prod - CERTIFICATE_ORGANIZATION: ${{ vars.CERTIFICATE_ORGANIZATION }} - CERTIFICATE_HOST: ${{ github.ref == 'refs/heads/master' && vars.HOST_API_SUBDOMAIN + '.' + vars.PRODUCTION_HOST || vars.HOST_API_SUBDOMAIN + '.' + vars.STAGING_HOST }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/certificate.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/certificate.tmp.yaml; - sudo kubectl apply -f .kubernetes/certificate.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/ingress.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/ingress.tmp.yaml; - sudo kubectl apply -f .kubernetes/ingress.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Hosting.Https/.gitignore b/Api.Hosting.Https/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Hosting.Https/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Hosting.Https/.kubernetes/autoscaler.yaml b/Api.Hosting.Https/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Hosting.Https/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Hosting.Https/.kubernetes/certificate.yaml b/Api.Hosting.Https/.kubernetes/certificate.yaml deleted file mode 100644 index a37bdadc..00000000 --- a/Api.Hosting.Https/.kubernetes/certificate.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: %SERVICE_NAME%-nginx-tls - namespace: %KUBERNETES_NAMESPACE% -spec: - secretName: %CERTIFICATE_HOST%-tls - duration: 2160h - renewBefore: 720h - subject: - organizations: - - %CERTIFICATE_ORGANIZATION% - dnsNames: - - %CERTIFICATE_HOST% - privateKey: - rotationPolicy: Always - issuerRef: - name: %CERTIFICATE_ISSUER% - kind: ClusterIssuer diff --git a/Api.Hosting.Https/.kubernetes/configmap.yaml b/Api.Hosting.Https/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Hosting.Https/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Hosting.Https/.kubernetes/deployment.yaml b/Api.Hosting.Https/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Hosting.Https/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Hosting.Https/.kubernetes/ingress.yaml b/Api.Hosting.Https/.kubernetes/ingress.yaml deleted file mode 100644 index 0acc7163..00000000 --- a/Api.Hosting.Https/.kubernetes/ingress.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ingress-%SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ingressClassName: nginx - tls: - - hosts: - - %CERTIFICATE_HOST% - secretName: %CERTIFICATE_HOST%-tls - rules: - - host: %CERTIFICATE_HOST% - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: %SERVICE_NAME% - port: - number: 8080 diff --git a/Api.Hosting.Https/.kubernetes/service.yaml b/Api.Hosting.Https/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Hosting.Https/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Properties/DoNotParallelize.cs b/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Tests.Api.Hosting.Https.csproj b/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Tests.Api.Hosting.Https.csproj deleted file mode 100644 index eff632ef..00000000 --- a/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Tests.Api.Hosting.Https.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj b/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj deleted file mode 100644 index 7a4d41f3..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj +++ /dev/null @@ -1,74 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https.sln b/Api.Hosting.Https/Api.Hosting.Https.sln deleted file mode 100644 index a0cf61cd..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https.sln +++ /dev/null @@ -1,136 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - localhost.pfx = localhost.pfx - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\certificate.yaml = .kubernetes\certificate.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\ingress.yaml = .kubernetes\ingress.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.Https.Models", "Api.Hosting.Https.Models\Api.Hosting.Https.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.Https", "Api.Hosting.Https\Api.Hosting.Https.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Hosting.Https", ".tests\Tests.Api.Hosting.Https\Tests.Api.Hosting.Https.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Hosting.Https/Api.Hosting.Https/Api.Hosting.Https.csproj b/Api.Hosting.Https/Api.Hosting.Https/Api.Hosting.Https.csproj deleted file mode 100644 index c5f99abc..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/Api.Hosting.Https.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/Controllers/ExamplesController.cs b/Api.Hosting.Https/Api.Hosting.Https/Controllers/ExamplesController.cs deleted file mode 100644 index ab1e443f..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Hosting.Https.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Https Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("https")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task HttpsAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("https"); - } -} \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local b/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local deleted file mode 100644 index e15900e2..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 4443 - -ENTRYPOINT ["dotnet", "Api.Hosting.Https.dll"] \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/Program.cs b/Api.Hosting.Https/Api.Hosting.Https/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Hosting.Https/Api.Hosting.Https/Properties/InternalsVisibleTo.cs b/Api.Hosting.Https/Api.Hosting.Https/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 34b77663..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Hosting.Https")] \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json deleted file mode 100644 index 23bfb871..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "App": { - "Hosting": { - "http": { - "UseHttpsRedirection": true - }, - "Https": { - "Ports": [ - 4443 - ], - "Certificate": { - "Path": "/root/.dotnet/https/localhost.pfx", - "Password": "password" - }, - "UseHttpsRequired": true - } - } - } -} \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Production.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Staging.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.Hosting.Https/Api.Hosting.Https/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.Hosting.Https/Dockerfile b/Api.Hosting.Https/Dockerfile deleted file mode 100644 index ed3c969f..00000000 --- a/Api.Hosting.Https/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Hosting.Https.dll"] \ No newline at end of file diff --git a/Api.Hosting.Https/LICENSE b/Api.Hosting.Https/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Hosting.Https/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Hosting.Https/README.md b/Api.Hosting.Https/README.md deleted file mode 100644 index 019d09bd..00000000 --- a/Api.Hosting.Https/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# Api.Hosting.Https - -> _Nano API application with https._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#gitHub-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example simply shows configuring a Nano application for HTTPS exposure. - -It adds HTTPS configuration, including a `localhost.pfx` self-signed development certificate, and a simple test controller that inherits -from the top-level Nano `BaseController`. - -The following endpoints are available for testing: - -| Endpoint | Description | -| -------------------------------------------- | -------------------------------------- | -| `http://localhost:8080/api/examples/http` | Redirects to HTTPS. | -| `https://localhost:4443/api/examples/https` | Returns a simple `200 OK` response. | - -> 📖 Learn more about **[Nano Hosting HTTPS](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#https)**. - -## Configuration -For `appsettings.json`, nothing has changed - HTTP is still exposed. -HTTPS and the development certificate are only configured in `appsettings.Development.json`. In live environments, HTTPS is handled by the ingress -in Kubernetes. Certificates are managed externally, and the service only needs to expose an HTTP port in `service.yaml` for mapping traffic through the ingress, -which serves HTTPS and forwards it to HTTP. - -```json -"App": { - "Hosting": { - "http": { - "UseHttpsRedirection": true - }, - "Https": { - "Ports": [ - 4443 - ], - "Certificate": { - "Path": "/root/.dotnet/https/localhost.pfx", - "Password": "password" - }, - "UseHttpsRequired": true - } - } -} -``` - -## Docker-compose -Added the following port and certificate path mapping to `docker-compose.yml`. - -```yaml -services: - nano.api.hosting.https: - ports: - - 4443:4443 - volumes: - - ../:/root/.dotnet/https -``` - -## Kubernetes -A `certificate.yaml` and a `ingress.yml` resource has been added to the `.kubernetes` folder. - -| File / Directory | Type | Description | -| -------------------- | ------- | -------------------------------------- | -| `ingress.yaml` | `yaml` | The ingress spec for Kubernetes. | -| `certificate.yaml` | `yaml` | The certificate spec for Kuberentes. | - - -## GitHub Actions -Additional environment variables have been added to `build-and-deploy.yml` to support the new Kubernetes resources. - -```yaml -env: - CERTIFICATE_ISSUER: letsencrypt-prod - CERTIFICATE_ORGANIZATION: ${{ vars.CERTIFICATE_ORGANIZATION }} - CERTIFICATE_HOST: ${{ github.ref == 'refs/heads/master' && vars.HOST_API_SUBDOMAIN + '.' + vars.PRODUCTION_HOST || vars.HOST_API_SUBDOMAIN + '.' + vars.STAGING_HOST }} -``` - -Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Api.Hosting.Https/icon.png b/Api.Hosting.Https/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 QtUG7zSmhZ)eg(jP05{mZ`~Uy| diff --git a/Api.Hosting.MultipartLimits/.docker/docker-compose.dcproj b/Api.Hosting.MultipartLimits/.docker/docker-compose.dcproj deleted file mode 100644 index d9e8e500..00000000 --- a/Api.Hosting.MultipartLimits/.docker/docker-compose.dcproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/.docker/docker-compose.yml b/Api.Hosting.MultipartLimits/.docker/docker-compose.yml deleted file mode 100644 index 5fe0aef5..00000000 --- a/Api.Hosting.MultipartLimits/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.hosting.multipartlimits: - image: api.hosting.multipartlimits - hostname: api-hosting-multipartlimits - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Hosting.MultipartLimits - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Hosting.MultipartLimits/.dockerignore b/Api.Hosting.MultipartLimits/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Hosting.MultipartLimits/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Hosting.MultipartLimits/.github/config/slack.yml b/Api.Hosting.MultipartLimits/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Hosting.MultipartLimits/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml b/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 4161a759..00000000 --- a/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Hosting.MultipartLimits - IMAGE_NAME: api.hosting.multipartlimits - SERVICE_NAME: api-Hosting-multipartlimits - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/.gitignore b/Api.Hosting.MultipartLimits/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Hosting.MultipartLimits/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/.kubernetes/autoscaler.yaml b/Api.Hosting.MultipartLimits/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Hosting.MultipartLimits/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml b/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml b/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Hosting.MultipartLimits/.kubernetes/service.yaml b/Api.Hosting.MultipartLimits/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Hosting.MultipartLimits/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Properties/DoNotParallelize.cs b/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Tests.Api.Hosting.MultipartLimits.csproj b/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Tests.Api.Hosting.MultipartLimits.csproj deleted file mode 100644 index cf2d7bec..00000000 --- a/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Tests.Api.Hosting.MultipartLimits.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.sln b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.sln deleted file mode 100644 index a6d173be..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.sln +++ /dev/null @@ -1,140 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.MultipartLimits.Models", "Api.Hosting.MultipartLimits.Models\Api.Hosting.MultipartLimits.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Microsoft", "..\..\Nano.Library\Nano.Logging.Microsoft\Nano.Logging.Microsoft.csproj", "{FBFE42A1-83A4-7281-4D54-7F520A06C20F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.MultipartLimits", "Api.Hosting.MultipartLimits\Api.Hosting.MultipartLimits.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Hosting.MultipartLimits", ".tests\Tests.Api.Hosting.MultipartLimits\Tests.Api.Hosting.MultipartLimits.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {FBFE42A1-83A4-7281-4D54-7F520A06C20F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FBFE42A1-83A4-7281-4D54-7F520A06C20F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FBFE42A1-83A4-7281-4D54-7F520A06C20F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FBFE42A1-83A4-7281-4D54-7F520A06C20F}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {FBFE42A1-83A4-7281-4D54-7F520A06C20F} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.csproj b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.csproj deleted file mode 100644 index 9463c71f..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Controllers/ExamplesController.cs b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Controllers/ExamplesController.cs deleted file mode 100644 index 1b715a0a..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Controllers/ExamplesController.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Common.Consts; - -namespace Api.Hosting.MultipartLimits.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Upload File. - /// Max size: 1 MB. - /// - /// The file. - /// The token used when request is cancelled. - /// A message. - /// OK. - [HttpPost] - [Route("upload-file")] - [Consumes(HttpContentType.FORM)] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task UploadFileAsync([Required]IFormFile file, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok($"File: '{file.FileName}' Uploadeed. Size: {file.Length} bytes."); - } -} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local deleted file mode 100644 index e80d58e4..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Hosting.MultipartLimits.dll"] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Program.cs b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Properties/InternalsVisibleTo.cs b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Properties/InternalsVisibleTo.cs deleted file mode 100644 index cc930f5f..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Hosting.MultipartLimits")] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Development.json b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Production.json b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Staging.json b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.json b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.json deleted file mode 100644 index 7d4c0efc..00000000 --- a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - }, - "MultipartLimits": { - "MaxUploadBytes": 1048576, - "KeepAliveTimeout": 30 - } - } - } -} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Dockerfile b/Api.Hosting.MultipartLimits/Dockerfile deleted file mode 100644 index b205db82..00000000 --- a/Api.Hosting.MultipartLimits/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Hosting.MultipartLimits.dll"] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/LICENSE b/Api.Hosting.MultipartLimits/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Hosting.MultipartLimits/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/README.md b/Api.Hosting.MultipartLimits/README.md deleted file mode 100644 index b8b04620..00000000 --- a/Api.Hosting.MultipartLimits/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Api.Hosting.MultipartLimits - -> Nano API application with upload limits._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example demonstrates upload limits configured for a Nano application. - -Added multipart limits for file uploads, setting max upload size to 1 MB. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ------------------------------------------------- | -------------------------------------------------------- | -| `http://localhost:8080/api/examples/upload-file` | Upload a file, if larger than 1 MB it will be rejected. | - -> 📖 Learn more about **[Nano MultiPart Limits](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Apimultipart-limits)**. - -## Configuration -Added the following configuration to `appsettings.json`. - -```json -Added - "App": { - "Hosting": { - "MultipartLimits": { - "MaxUploadBytes": 1048576, - "KeepAliveTimeout": 30 - } -} -``` diff --git a/Api.Hosting.MultipartLimits/icon.png b/Api.Hosting.MultipartLimits/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Localization/.docker/docker-compose.yml b/Api.Localization/.docker/docker-compose.yml deleted file mode 100644 index 0af53bf1..00000000 --- a/Api.Localization/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.localization: - image: api.localization - hostname: api-localization - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Localization - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Localization/.dockerignore b/Api.Localization/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Localization/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Localization/.github/config/slack.yml b/Api.Localization/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Localization/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Localization/.github/workflows/build-and-deploy.yml b/Api.Localization/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 9c109c22..00000000 --- a/Api.Localization/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Localization - IMAGE_NAME: api.localization - SERVICE_NAME: api-localization - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Localization/.gitignore b/Api.Localization/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Localization/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Localization/.kubernetes/autoscaler.yaml b/Api.Localization/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Localization/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Localization/.kubernetes/configmap.yaml b/Api.Localization/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Localization/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Localization/.kubernetes/deployment.yaml b/Api.Localization/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Localization/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Localization/.kubernetes/service.yaml b/Api.Localization/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Localization/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Localization/.tests/Tests.Api.Localization/Properties/DoNotParallelize.cs b/Api.Localization/.tests/Tests.Api.Localization/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Localization/.tests/Tests.Api.Localization/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Localization/.tests/Tests.Api.Localization/Tests.Api.Localization.csproj b/Api.Localization/.tests/Tests.Api.Localization/Tests.Api.Localization.csproj deleted file mode 100644 index 118efd56..00000000 --- a/Api.Localization/.tests/Tests.Api.Localization/Tests.Api.Localization.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj b/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Localization/Api.Localization.sln b/Api.Localization/Api.Localization.sln deleted file mode 100644 index 49d5760f..00000000 --- a/Api.Localization/Api.Localization.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Localization.Models", "Api.Localization.Models\Api.Localization.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Localization", "Api.Localization\Api.Localization.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Localization", ".tests\Tests.Api.Localization\Tests.Api.Localization.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Localization/Api.Localization/Api.Localization.csproj b/Api.Localization/Api.Localization/Api.Localization.csproj deleted file mode 100644 index e7c0acea..00000000 --- a/Api.Localization/Api.Localization/Api.Localization.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Localization/Api.Localization/Controllers/ExamplesController.cs b/Api.Localization/Api.Localization/Controllers/ExamplesController.cs deleted file mode 100644 index 7d96f751..00000000 --- a/Api.Localization/Api.Localization/Controllers/ExamplesController.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Globalization; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Localization.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Localization Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("localization")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LocalizationAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - - return this.Ok(new - { - CultureInfo.CurrentCulture.Name, - CultureInfo.CurrentCulture.EnglishName, - CultureInfo.CurrentCulture.NativeName - }); - } -} \ No newline at end of file diff --git a/Api.Localization/Api.Localization/Dockerfile.Local b/Api.Localization/Api.Localization/Dockerfile.Local deleted file mode 100644 index 4cb9e786..00000000 --- a/Api.Localization/Api.Localization/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Localization.dll"] \ No newline at end of file diff --git a/Api.Localization/Api.Localization/Program.cs b/Api.Localization/Api.Localization/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Localization/Api.Localization/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Localization/Api.Localization/Properties/InternalsVisibleTo.cs b/Api.Localization/Api.Localization/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 118e4e92..00000000 --- a/Api.Localization/Api.Localization/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Localization")] \ No newline at end of file diff --git a/Api.Localization/Api.Localization/appsettings.Development.json b/Api.Localization/Api.Localization/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Localization/Api.Localization/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Localization/Api.Localization/appsettings.Production.json b/Api.Localization/Api.Localization/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Localization/Api.Localization/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Localization/Api.Localization/appsettings.Staging.json b/Api.Localization/Api.Localization/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Localization/Api.Localization/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Localization/Api.Localization/appsettings.json b/Api.Localization/Api.Localization/appsettings.json deleted file mode 100644 index 218abec2..00000000 --- a/Api.Localization/Api.Localization/appsettings.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Localization": { - "DefaultCulture": "en-US", - "SupportedCultures": [ - "da-DK" - ] - } - } -} \ No newline at end of file diff --git a/Api.Localization/Dockerfile b/Api.Localization/Dockerfile deleted file mode 100644 index 14247453..00000000 --- a/Api.Localization/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Localization.dll"] \ No newline at end of file diff --git a/Api.Localization/LICENSE b/Api.Localization/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Localization/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Localization/README.md b/Api.Localization/README.md deleted file mode 100644 index 8e934551..00000000 --- a/Api.Localization/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Api.Localization - -> _Nano API application with request localization._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example shows how to use localized requests in Nano. - -The controller return a response in the following format: - -```json -{ - "Name": "Danish", // The name of the culture language - "EnglishName": "Danish", // The english name of the culture language - "NativeName": "Dansk", // The native name of the culture language -} -``` - -| Endpoint | Description | -| ------------------------------------------------- | ---------------------------------------------------------------- | -| `http://localhost:8080/api/examples/localization` | Returns a `200 OK` response with names of the culture langauge. | - -> 📖 Learn more about **[Nano Localization](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#localization)**. - -## Configuration - -```json -"App": { - "Localization": { - "DefaultCulture": "en-US", - "SupportedCultures": [ - "da-DK" - ] - } -} -``` \ No newline at end of file diff --git a/Api.Localization/icon.png b/Api.Localization/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Logging.Log4Net/.docker/docker-compose.yml b/Api.Logging.Log4Net/.docker/docker-compose.yml deleted file mode 100644 index d0ceb01b..00000000 --- a/Api.Logging.Log4Net/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.logging.log4net: - image: api.logging.log4net - hostname: api-logging-log4net - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Logging.Log4Net - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Logging.Log4Net/.dockerignore b/Api.Logging.Log4Net/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Logging.Log4Net/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Logging.Log4Net/.github/config/slack.yml b/Api.Logging.Log4Net/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Logging.Log4Net/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml b/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml deleted file mode 100644 index f0652e64..00000000 --- a/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Logging.Log4Net - IMAGE_NAME: api.logging.log4net - SERVICE_NAME: api-logging-log4net - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Logging.Log4Net/.gitignore b/Api.Logging.Log4Net/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Logging.Log4Net/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Logging.Log4Net/.kubernetes/autoscaler.yaml b/Api.Logging.Log4Net/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Logging.Log4Net/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Logging.Log4Net/.kubernetes/configmap.yaml b/Api.Logging.Log4Net/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Logging.Log4Net/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Logging.Log4Net/.kubernetes/deployment.yaml b/Api.Logging.Log4Net/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Logging.Log4Net/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Logging.Log4Net/.kubernetes/service.yaml b/Api.Logging.Log4Net/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Logging.Log4Net/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Properties/DoNotParallelize.cs b/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Tests.Api.Logging.Log4Net.csproj b/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Tests.Api.Logging.Log4Net.csproj deleted file mode 100644 index 247b359b..00000000 --- a/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Tests.Api.Logging.Log4Net.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj b/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj deleted file mode 100644 index 6d16235d..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net.sln b/Api.Logging.Log4Net/Api.Logging.Log4Net.sln deleted file mode 100644 index 0dfddf52..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Log4Net.Models", "Api.Logging.Log4Net.Models\Api.Logging.Log4Net.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Log4Net", "Api.Logging.Log4Net\Api.Logging.Log4Net.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Logging.Log4Net", ".tests\Tests.Api.Logging.Log4Net\Tests.Api.Logging.Log4Net.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Log4Net", "..\..\Nano.Library\Nano.Logging.Log4Net\Nano.Logging.Log4Net.csproj", "{FB775619-7CDE-E029-14F8-7C22944D6DC9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {68215D72-880F-683C-BFEE-ED19A425A5F2} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {FB775619-7CDE-E029-14F8-7C22944D6DC9} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Api.Logging.Log4Net.csproj b/Api.Logging.Log4Net/Api.Logging.Log4Net/Api.Logging.Log4Net.csproj deleted file mode 100644 index 256bcf34..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/Api.Logging.Log4Net.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Controllers/ExamplesController.cs b/Api.Logging.Log4Net/Api.Logging.Log4Net/Controllers/ExamplesController.cs deleted file mode 100644 index 86df96a7..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/Controllers/ExamplesController.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Logging.Log4Net.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Logging Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("logging")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LoggingAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - this.Logger - .LogDebug("Debug"); - - this.Logger - .LogInformation("Information"); - - this.Logger - .LogWarning("Warning"); - - this.Logger - .LogError("Error"); - - return this.Ok("logging"); - } - - /// - /// Logging Exception Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("logging-exception")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LoggingExceptionAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new Exception("test error"); - } -} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local b/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local deleted file mode 100644 index 722f4503..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Program.cs b/Api.Logging.Log4Net/Api.Logging.Log4Net/Program.cs deleted file mode 100644 index 5f5829b2..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Api; -using Nano.Logging.Extensions; -using Nano.Logging.Log4Net; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoLogging(); - }) - .Build() - .Run(); diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Properties/InternalsVisibleTo.cs b/Api.Logging.Log4Net/Api.Logging.Log4Net/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 057f5be8..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Logging.Log4Net")] \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Development.json b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Production.json b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Staging.json b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.json b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.json deleted file mode 100644 index 97b325d7..00000000 --- a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] - } -} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Dockerfile b/Api.Logging.Log4Net/Dockerfile deleted file mode 100644 index 8ba64365..00000000 --- a/Api.Logging.Log4Net/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Api.Logging.Log4Net/LICENSE b/Api.Logging.Log4Net/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Logging.Log4Net/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Logging.Log4Net/README.md b/Api.Logging.Log4Net/README.md deleted file mode 100644 index 956a56ea..00000000 --- a/Api.Logging.Log4Net/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Api.Logging.Log4Net - -> _Nano API application with Log4Net logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This application demonstrates logging with Log4Net for a API application. Also note the `LogLevelOverrides` configuration, -where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. - -The following endpoint is available for testing: - -| Endpoint | Description | -| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/logging` | Returns a simple `200 OK` response. Won't log the `.LogDebug(...)` due to configuration `LogLevel=Information`. | -| `http://localhost:8080/api/examples/logging-exception` | Returns a simple `500 Internal Server Error` response. The exception will be logged. | - -> 📖 Learn more about **[Nano.Logging.Log4Net](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Log4Net)**. - -## Registration -The following logging has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoLogging(); -}) -... -``` - -## Configuration -Configured the application with the necessary logging setup. - -```json -"Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] -} -``` diff --git a/Api.Logging.Log4Net/icon.png b/Api.Logging.Log4Net/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Logging.Microsoft/.docker/docker-compose.yml b/Api.Logging.Microsoft/.docker/docker-compose.yml deleted file mode 100644 index be89bf7c..00000000 --- a/Api.Logging.Microsoft/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.logging.microsoft: - image: api.logging.microsoft - hostname: api-logging-microsoft - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Logging.Microsoft - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Logging.Microsoft/.dockerignore b/Api.Logging.Microsoft/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Logging.Microsoft/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Logging.Microsoft/.github/config/slack.yml b/Api.Logging.Microsoft/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Logging.Microsoft/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml b/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 5cf18938..00000000 --- a/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Logging.Microsoft - IMAGE_NAME: api.logging.microsoft - SERVICE_NAME: api-logging-microsoft - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Logging.Microsoft/.gitignore b/Api.Logging.Microsoft/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Logging.Microsoft/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Logging.Microsoft/.kubernetes/autoscaler.yaml b/Api.Logging.Microsoft/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Logging.Microsoft/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Logging.Microsoft/.kubernetes/configmap.yaml b/Api.Logging.Microsoft/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Logging.Microsoft/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Logging.Microsoft/.kubernetes/deployment.yaml b/Api.Logging.Microsoft/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Logging.Microsoft/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Logging.Microsoft/.kubernetes/service.yaml b/Api.Logging.Microsoft/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Logging.Microsoft/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Properties/DoNotParallelize.cs b/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Tests.Api.Logging.Microsoft.csproj b/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Tests.Api.Logging.Microsoft.csproj deleted file mode 100644 index 8ecfe29d..00000000 --- a/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Tests.Api.Logging.Microsoft.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj b/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj deleted file mode 100644 index a284b2dc..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft.sln b/Api.Logging.Microsoft/Api.Logging.Microsoft.sln deleted file mode 100644 index e6202494..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Microsoft.Models", "Api.Logging.Microsoft.Models\Api.Logging.Microsoft.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Microsoft", "Api.Logging.Microsoft\Api.Logging.Microsoft.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Logging.Microsoft", ".tests\Tests.Api.Logging.Microsoft\Tests.Api.Logging.Microsoft.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Microsoft", "..\..\Nano.Library\Nano.Logging.Microsoft\Nano.Logging.Microsoft.csproj", "{FB775619-7CDE-E029-14F8-7C22944D6DC9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {68215D72-880F-683C-BFEE-ED19A425A5F2} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {FB775619-7CDE-E029-14F8-7C22944D6DC9} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Api.Logging.Microsoft.csproj b/Api.Logging.Microsoft/Api.Logging.Microsoft/Api.Logging.Microsoft.csproj deleted file mode 100644 index 5d7ac8da..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/Api.Logging.Microsoft.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Controllers/ExamplesController.cs b/Api.Logging.Microsoft/Api.Logging.Microsoft/Controllers/ExamplesController.cs deleted file mode 100644 index 1406e8b5..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/Controllers/ExamplesController.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Logging.Microsoft.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Logging Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("logging")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LoggingAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - this.Logger - .LogDebug("Debug"); - - this.Logger - .LogInformation("Information"); - - this.Logger - .LogWarning("Warning"); - - this.Logger - .LogError("Error"); - - return this.Ok("logging"); - } - - /// - /// Logging Exception Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("logging-exception")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LoggingExceptionAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new Exception("test error"); - } -} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local b/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local deleted file mode 100644 index adaac8c3..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Program.cs b/Api.Logging.Microsoft/Api.Logging.Microsoft/Program.cs deleted file mode 100644 index 93786083..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Api; -using Nano.Logging.Extensions; -using Nano.Logging.Microsoft; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoLogging(); - }) - .Build() - .Run(); diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Properties/InternalsVisibleTo.cs b/Api.Logging.Microsoft/Api.Logging.Microsoft/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 1916d193..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Logging.Microsoft")] \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Development.json b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Production.json b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Staging.json b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.json b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.json deleted file mode 100644 index 97b325d7..00000000 --- a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] - } -} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Dockerfile b/Api.Logging.Microsoft/Dockerfile deleted file mode 100644 index d77a0004..00000000 --- a/Api.Logging.Microsoft/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Api.Logging.Microsoft/LICENSE b/Api.Logging.Microsoft/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Logging.Microsoft/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Logging.Microsoft/README.md b/Api.Logging.Microsoft/README.md deleted file mode 100644 index 23c9e8e7..00000000 --- a/Api.Logging.Microsoft/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Api.Logging.Microsoft - -> _Nano API application with Microsoft logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This application demonstrates logging with Microsoft for a API application. Also note the `LogLevelOverrides` configuration, -where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. - -The following endpoint is available for testing: - -| Endpoint | Description | -| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/logging` | Returns a simple `200 OK` response. Won't log the `.LogDebug(...)` due to configuration `LogLevel=Information`. | -| `http://localhost:8080/api/examples/logging-exception` | Returns a simple `500 Internal Server Error` response. The exception will be logged. | - -> 📖 Learn more about **[Nano.Logging.Microsoft](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Microsoft)**. - -## Registration -The following logging has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoLogging(); -}) -... -``` - -## Configuration -Configured the application with the necessary logging setup. - -```json -"Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] -} -``` diff --git a/Api.Logging.Microsoft/icon.png b/Api.Logging.Microsoft/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Logging.NLog/.docker/docker-compose.yml b/Api.Logging.NLog/.docker/docker-compose.yml deleted file mode 100644 index 0922a6b5..00000000 --- a/Api.Logging.NLog/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.logging.nlog: - image: api.logging.nlog - hostname: api-logging-nlog - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Logging.NLog - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Logging.NLog/.dockerignore b/Api.Logging.NLog/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Logging.NLog/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Logging.NLog/.github/config/slack.yml b/Api.Logging.NLog/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Logging.NLog/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Logging.NLog/.github/workflows/build-and-deploy.yml b/Api.Logging.NLog/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 5d0bd0d0..00000000 --- a/Api.Logging.NLog/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Logging.NLog - IMAGE_NAME: api.logging.nlog - SERVICE_NAME: api-logging-nlog - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Logging.NLog/.gitignore b/Api.Logging.NLog/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Logging.NLog/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Logging.NLog/.kubernetes/autoscaler.yaml b/Api.Logging.NLog/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Logging.NLog/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Logging.NLog/.kubernetes/configmap.yaml b/Api.Logging.NLog/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Logging.NLog/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Logging.NLog/.kubernetes/deployment.yaml b/Api.Logging.NLog/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Logging.NLog/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Logging.NLog/.kubernetes/service.yaml b/Api.Logging.NLog/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Logging.NLog/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Properties/DoNotParallelize.cs b/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Tests.Api.Logging.NLog.csproj b/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Tests.Api.Logging.NLog.csproj deleted file mode 100644 index 63c37b06..00000000 --- a/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Tests.Api.Logging.NLog.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj b/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj deleted file mode 100644 index 8989e105..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog.sln b/Api.Logging.NLog/Api.Logging.NLog.sln deleted file mode 100644 index d5a0c759..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.NLog.Models", "Api.Logging.NLog.Models\Api.Logging.NLog.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.NLog", "Api.Logging.NLog\Api.Logging.NLog.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Logging.NLog", ".tests\Tests.Api.Logging.NLog\Tests.Api.Logging.NLog.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.NLog", "..\..\Nano.Library\Nano.Logging.NLog\Nano.Logging.NLog.csproj", "{FB775619-7CDE-E029-14F8-7C22944D6DC9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {68215D72-880F-683C-BFEE-ED19A425A5F2} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {FB775619-7CDE-E029-14F8-7C22944D6DC9} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Logging.NLog/Api.Logging.NLog/Api.Logging.NLog.csproj b/Api.Logging.NLog/Api.Logging.NLog/Api.Logging.NLog.csproj deleted file mode 100644 index 5a8b6ea6..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/Api.Logging.NLog.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/Controllers/ExamplesController.cs b/Api.Logging.NLog/Api.Logging.NLog/Controllers/ExamplesController.cs deleted file mode 100644 index 85dfb8bb..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/Controllers/ExamplesController.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Logging.NLog.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Logging Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("logging")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LoggingAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - this.Logger - .LogDebug("Debug"); - - this.Logger - .LogInformation("Information"); - - this.Logger - .LogWarning("Warning"); - - this.Logger - .LogError("Error"); - - return this.Ok("logging"); - } - - /// - /// Logging Exception Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("logging-exception")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LoggingExceptionAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new Exception("test error"); - } -} \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local b/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local deleted file mode 100644 index e47db8d4..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Logging.NLog.dll"] \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/Program.cs b/Api.Logging.NLog/Api.Logging.NLog/Program.cs deleted file mode 100644 index 659ee02b..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Api; -using Nano.Logging.Extensions; -using Nano.Logging.NLog; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoLogging(); - }) - .Build() - .Run(); diff --git a/Api.Logging.NLog/Api.Logging.NLog/Properties/InternalsVisibleTo.cs b/Api.Logging.NLog/Api.Logging.NLog/Properties/InternalsVisibleTo.cs deleted file mode 100644 index ab204547..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Logging.NLog")] \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Development.json b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Production.json b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Staging.json b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/appsettings.json b/Api.Logging.NLog/Api.Logging.NLog/appsettings.json deleted file mode 100644 index 97b325d7..00000000 --- a/Api.Logging.NLog/Api.Logging.NLog/appsettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] - } -} \ No newline at end of file diff --git a/Api.Logging.NLog/Dockerfile b/Api.Logging.NLog/Dockerfile deleted file mode 100644 index f7f28f9d..00000000 --- a/Api.Logging.NLog/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Logging.NLog.dll"] \ No newline at end of file diff --git a/Api.Logging.NLog/LICENSE b/Api.Logging.NLog/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Logging.NLog/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Logging.NLog/README.md b/Api.Logging.NLog/README.md deleted file mode 100644 index 80b79ee2..00000000 --- a/Api.Logging.NLog/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Api.Logging.NLog - -> _Nano API application with NLog logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This application demonstrates logging with NLog for a API application. Also note the `LogLevelOverrides` configuration, -where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/logging` | Returns a simple `200 OK` response. Won't log the `.LogDebug(...)` due to configuration `LogLevel=Information`. | -| `http://localhost:8080/api/examples/logging-exception` | Returns a simple `500 Internal Server Error` response. The exception will be logged. | - -> 📖 Learn more about **[Nano.Logging.NLog](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.NLog)**. - -## Registration -The following logging has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoLogging(); -}) -... -``` - -## Configuration -Configured the application with the necessary logging setup. - -```json -"Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] -} -``` diff --git a/Api.Logging.NLog/icon.png b/Api.Logging.NLog/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Logging.Serilog/.docker/docker-compose.yml b/Api.Logging.Serilog/.docker/docker-compose.yml deleted file mode 100644 index c90a936c..00000000 --- a/Api.Logging.Serilog/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.logging.serilog: - image: api.logging.serilog - hostname: api-logging-serilog - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Logging.Serilog - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Logging.Serilog/.dockerignore b/Api.Logging.Serilog/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Logging.Serilog/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Logging.Serilog/.github/config/slack.yml b/Api.Logging.Serilog/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Logging.Serilog/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml b/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 1e9e649f..00000000 --- a/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Logging.Serilog - IMAGE_NAME: api.logging.serilog - SERVICE_NAME: api-logging-serilog - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Logging.Serilog/.gitignore b/Api.Logging.Serilog/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Logging.Serilog/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Logging.Serilog/.kubernetes/autoscaler.yaml b/Api.Logging.Serilog/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Logging.Serilog/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Logging.Serilog/.kubernetes/configmap.yaml b/Api.Logging.Serilog/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Logging.Serilog/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Logging.Serilog/.kubernetes/deployment.yaml b/Api.Logging.Serilog/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Logging.Serilog/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Logging.Serilog/.kubernetes/service.yaml b/Api.Logging.Serilog/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Logging.Serilog/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Properties/DoNotParallelize.cs b/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Tests.Api.Logging.Serilog.csproj b/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Tests.Api.Logging.Serilog.csproj deleted file mode 100644 index 9abdeec5..00000000 --- a/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Tests.Api.Logging.Serilog.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj b/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj deleted file mode 100644 index d1fa3771..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog.sln b/Api.Logging.Serilog/Api.Logging.Serilog.sln deleted file mode 100644 index d9108a28..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Serilog.Models", "Api.Logging.Serilog.Models\Api.Logging.Serilog.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Serilog", "Api.Logging.Serilog\Api.Logging.Serilog.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Logging.Serilog", ".tests\Tests.Api.Logging.Serilog\Tests.Api.Logging.Serilog.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Serilog", "..\..\Nano.Library\Nano.Logging.Serilog\Nano.Logging.Serilog.csproj", "{FB775619-7CDE-E029-14F8-7C22944D6DC9}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {68215D72-880F-683C-BFEE-ED19A425A5F2} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {FB775619-7CDE-E029-14F8-7C22944D6DC9} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Api.Logging.Serilog.csproj b/Api.Logging.Serilog/Api.Logging.Serilog/Api.Logging.Serilog.csproj deleted file mode 100644 index 1ee7daba..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/Api.Logging.Serilog.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Controllers/ExamplesController.cs b/Api.Logging.Serilog/Api.Logging.Serilog/Controllers/ExamplesController.cs deleted file mode 100644 index 86db1ecb..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/Controllers/ExamplesController.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.Logging.Serilog.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Logging Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("logging")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LoggingAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - this.Logger - .LogDebug("Debug"); - - this.Logger - .LogInformation("Information"); - - this.Logger - .LogWarning("Warning"); - - this.Logger - .LogError("Error"); - - return this.Ok("logging"); - } - - /// - /// Logging Exception Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("logging-exception")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task LoggingExceptionAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - throw new Exception("test error"); - } -} \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local b/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local deleted file mode 100644 index 006fb412..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Program.cs b/Api.Logging.Serilog/Api.Logging.Serilog/Program.cs deleted file mode 100644 index d2ab08f7..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Api; -using Nano.Logging.Extensions; -using Nano.Logging.Serilog; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoLogging(); - }) - .Build() - .Run(); diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Properties/InternalsVisibleTo.cs b/Api.Logging.Serilog/Api.Logging.Serilog/Properties/InternalsVisibleTo.cs deleted file mode 100644 index ca569d77..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Logging.Serilog")] \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Development.json b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Production.json b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Staging.json b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.json b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.json deleted file mode 100644 index 97b325d7..00000000 --- a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - }, - "Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] - } -} \ No newline at end of file diff --git a/Api.Logging.Serilog/Dockerfile b/Api.Logging.Serilog/Dockerfile deleted file mode 100644 index e211ed1c..00000000 --- a/Api.Logging.Serilog/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Api.Logging.Serilog/LICENSE b/Api.Logging.Serilog/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Logging.Serilog/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Logging.Serilog/README.md b/Api.Logging.Serilog/README.md deleted file mode 100644 index c84a62b3..00000000 --- a/Api.Logging.Serilog/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Api.Logging.Serilog - -> _Nano API application with serilog logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This application demonstrates logging with Serilog for a API application. Also note the `LogLevelOverrides` configuration, -where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. - -The following endpoint is available for testing: - -| Endpoint | Description | -| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/logging` | Returns a simple `200 OK` response. Won't log the `.LogDebug(...)` due to configuration `LogLevel=Information`. | -| `http://localhost:8080/api/examples/logging-exception` | Returns a simple `500 Internal Server Error` response. The exception will be logged. | - -> 📖 Learn more about **[Nano.Logging.Serilog](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Serilog)**. - -## Registration -The following logging has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoLogging(); -}) -... -``` - -## Configuration -Configured the application with the necessary logging setup. - -```json -"Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] -} -``` diff --git a/Api.Logging.Serilog/icon.png b/Api.Logging.Serilog/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.MultipartJson/.docker/docker-compose.yml b/Api.MultipartJson/.docker/docker-compose.yml deleted file mode 100644 index c4c85d9e..00000000 --- a/Api.MultipartJson/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.multipartjson: - image: api.multipartjson - hostname: api-multipartjson - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.MultipartJson - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.MultipartJson/.dockerignore b/Api.MultipartJson/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.MultipartJson/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.MultipartJson/.github/config/slack.yml b/Api.MultipartJson/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.MultipartJson/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.MultipartJson/.github/workflows/build-and-deploy.yml b/Api.MultipartJson/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 5a28fde8..00000000 --- a/Api.MultipartJson/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.MultipartJson - IMAGE_NAME: api.multipartjson - SERVICE_NAME: api-multipartjson - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.MultipartJson/.gitignore b/Api.MultipartJson/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.MultipartJson/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.MultipartJson/.kubernetes/autoscaler.yaml b/Api.MultipartJson/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.MultipartJson/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.MultipartJson/.kubernetes/configmap.yaml b/Api.MultipartJson/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.MultipartJson/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.MultipartJson/.kubernetes/deployment.yaml b/Api.MultipartJson/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.MultipartJson/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.MultipartJson/.kubernetes/service.yaml b/Api.MultipartJson/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.MultipartJson/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Properties/DoNotParallelize.cs b/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Tests.Api.MultipartJson.csproj b/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Tests.Api.MultipartJson.csproj deleted file mode 100644 index 96d385fd..00000000 --- a/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Tests.Api.MultipartJson.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj b/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson.sln b/Api.MultipartJson/Api.MultipartJson.sln deleted file mode 100644 index e22fe2c9..00000000 --- a/Api.MultipartJson/Api.MultipartJson.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.MultipartJson.Models", "Api.MultipartJson.Models\Api.MultipartJson.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.MultipartJson", "Api.MultipartJson\Api.MultipartJson.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.MultipartJson", ".tests\Tests.Api.MultipartJson\Tests.Api.MultipartJson.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.MultipartJson/Api.MultipartJson/Api.MultipartJson.csproj b/Api.MultipartJson/Api.MultipartJson/Api.MultipartJson.csproj deleted file mode 100644 index d9549228..00000000 --- a/Api.MultipartJson/Api.MultipartJson/Api.MultipartJson.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/Controllers/ExamplesController.cs b/Api.MultipartJson/Api.MultipartJson/Controllers/ExamplesController.cs deleted file mode 100644 index c88e36b9..00000000 --- a/Api.MultipartJson/Api.MultipartJson/Controllers/ExamplesController.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.MultipartJson.Controllers.Requests; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Annotations; -using Nano.App.Api.Controllers; -using Newtonsoft.Json; - -namespace Api.MultipartJson.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Multipart Json Action. - /// - /// The file. - /// The json body - /// The cancellation token. - /// A message. - /// Success. - [HttpPost] - [Route("multipart-json")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task MultipartJsonAsync(IFormFile file, [Required][FromFormBody]JsonBody body, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - Console.WriteLine(file.FileName); - Console.WriteLine(JsonConvert.SerializeObject(body)); - - return this.Ok("multipart-json"); - } -} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/Controllers/Requests/JsonBody.cs b/Api.MultipartJson/Api.MultipartJson/Controllers/Requests/JsonBody.cs deleted file mode 100644 index 2c2ffba9..00000000 --- a/Api.MultipartJson/Api.MultipartJson/Controllers/Requests/JsonBody.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Api.MultipartJson.Controllers.Requests; - -/// -/// Json Body. -/// -public class JsonBody -{ - /// - /// Text. - /// - [Required] - public string Text { get; set; } = null!; -} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local b/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local deleted file mode 100644 index 8d1d3884..00000000 --- a/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.MultipartJson.dll"] \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/Program.cs b/Api.MultipartJson/Api.MultipartJson/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.MultipartJson/Api.MultipartJson/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.MultipartJson/Api.MultipartJson/Properties/InternalsVisibleTo.cs b/Api.MultipartJson/Api.MultipartJson/Properties/InternalsVisibleTo.cs deleted file mode 100644 index ccb78b5f..00000000 --- a/Api.MultipartJson/Api.MultipartJson/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.MultipartJson")] \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/appsettings.Development.json b/Api.MultipartJson/Api.MultipartJson/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.MultipartJson/Api.MultipartJson/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/appsettings.Production.json b/Api.MultipartJson/Api.MultipartJson/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.MultipartJson/Api.MultipartJson/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/appsettings.Staging.json b/Api.MultipartJson/Api.MultipartJson/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.MultipartJson/Api.MultipartJson/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/appsettings.json b/Api.MultipartJson/Api.MultipartJson/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.MultipartJson/Api.MultipartJson/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.MultipartJson/Dockerfile b/Api.MultipartJson/Dockerfile deleted file mode 100644 index efb0b01a..00000000 --- a/Api.MultipartJson/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.MultipartJson.dll"] \ No newline at end of file diff --git a/Api.MultipartJson/LICENSE b/Api.MultipartJson/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.MultipartJson/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.MultipartJson/README.md b/Api.MultipartJson/README.md deleted file mode 100644 index e78b2c23..00000000 --- a/Api.MultipartJson/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# Api.MultipartJson - -> _Nano API application using request multipart json._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example simply shows uploading a file together with a JSON body. Observe the filename of the file is written to console, as well as the json body passed. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ---------------------------------------------------- | -------------------------------------- | -| `http://localhost:8080/api/examples/multipart-json` | Returns a simple `200 OK` response. | - -> 📖 Learn more about **[Nano Request Multipart JSON](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#request-multipart-json)**. diff --git a/Api.MultipartJson/icon.png b/Api.MultipartJson/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.yml b/Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.yml deleted file mode 100644 index 4d629227..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -services: - api.policyheaders.contentsecuritypolicy: - image: api.policyheaders.contentsecuritypolicy - hostname: api-policyheaders-contentsecuritypolicy - restart: on-failure - ports: - - 8080:8080 - - 4443:4443 - build: - context: ../Api.PolicyHeaders.ContentSecurityPolicy - dockerfile: "Dockerfile.Local" - volumes: - - ../:/root/.dotnet/https - networks: - - network - -networks: - network: - name: network - driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.dockerignore b/Api.PolicyHeaders.ContentSecurityPolicy/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.github/config/slack.yml b/Api.PolicyHeaders.ContentSecurityPolicy/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml deleted file mode 100644 index f6d4b5e8..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,188 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.PolicyHeaders.ContentSecurityPolicy - IMAGE_NAME: api.policyheaders.contentsecuritypolicy - SERVICE_NAME: api-policyheaders-contentsecuritypolicy - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - CERTIFICATE_ISSUER: letsencrypt-prod - CERTIFICATE_ORGANIZATION: ${{ vars.CERTIFICATE_ORGANIZATION }} - CERTIFICATE_HOST: ${{ github.ref == 'refs/heads/master' && vars.HOST_API_SUBDOMAIN + '.' + vars.PRODUCTION_HOST || vars.HOST_API_SUBDOMAIN + '.' + vars.STAGING_HOST }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/certificate.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/certificate.tmp.yaml; - sudo kubectl apply -f .kubernetes/certificate.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/ingress.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/ingress.tmp.yaml; - sudo kubectl apply -f .kubernetes/ingress.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.gitignore b/Api.PolicyHeaders.ContentSecurityPolicy/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/certificate.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/certificate.yaml deleted file mode 100644 index a37bdadc..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/certificate.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: %SERVICE_NAME%-nginx-tls - namespace: %KUBERNETES_NAMESPACE% -spec: - secretName: %CERTIFICATE_HOST%-tls - duration: 2160h - renewBefore: 720h - subject: - organizations: - - %CERTIFICATE_ORGANIZATION% - dnsNames: - - %CERTIFICATE_HOST% - privateKey: - rotationPolicy: Always - issuerRef: - name: %CERTIFICATE_ISSUER% - kind: ClusterIssuer diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/ingress.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/ingress.yaml deleted file mode 100644 index 0acc7163..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/ingress.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ingress-%SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ingressClassName: nginx - tls: - - hosts: - - %CERTIFICATE_HOST% - secretName: %CERTIFICATE_HOST%-tls - rules: - - host: %CERTIFICATE_HOST% - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: %SERVICE_NAME% - port: - number: 8080 diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/service.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj b/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj deleted file mode 100644 index 7d531b1d..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj deleted file mode 100644 index 890be2f1..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.sln b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.sln deleted file mode 100644 index ed2a826c..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - localhost.pfx = localhost.pfx - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\certificate.yaml = .kubernetes\certificate.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\ingress.yaml = .kubernetes\ingress.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ContentSecurityPolicy.Models", "Api.PolicyHeaders.ContentSecurityPolicy.Models\Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ContentSecurityPolicy", "Api.PolicyHeaders.ContentSecurityPolicy\Api.PolicyHeaders.ContentSecurityPolicy.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.ContentSecurityPolicy", ".tests\Tests.Api.PolicyHeaders.ContentSecurityPolicy\Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.csproj b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.csproj deleted file mode 100644 index e11f0b6f..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Controllers/ExamplesController.cs b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Controllers/ExamplesController.cs deleted file mode 100644 index 002e4989..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace Api.PolicyHeaders.ContentSecurityPolicy.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Csp Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("csp")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task CspAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("csp"); - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local deleted file mode 100644 index c6f98fe9..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 4443 - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentSecurityPolicy.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Program.cs b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Program.cs deleted file mode 100644 index ea31b138..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Api; - -// BUG: We need logging here because otherwise we don't see the logged csp-report - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Properties/InternalsVisibleTo.cs deleted file mode 100644 index e5d9e205..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Hosting.Http")] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json deleted file mode 100644 index 23bfb871..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "App": { - "Hosting": { - "http": { - "UseHttpsRedirection": true - }, - "Https": { - "Ports": [ - 4443 - ], - "Certificate": { - "Path": "/root/.dotnet/https/localhost.pfx", - "Password": "password" - }, - "UseHttpsRequired": true - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Production.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Staging.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.json deleted file mode 100644 index fabe077b..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HttpPolicyHeaders": { - "Csp": { - "ReportOnly": false, - "UpgradeInsecureRequests": true, - "Defaults": { - "IsNone": false, - "IsSelf": true, - "Sources": [ - "https://localhost" - ] - }, - "Scripts": { - "IsNone": false, - "IsSelf": true, - "IsUnsafeInline": false, - "IsUnsafeEval": false, - "IsUnsafeWasmEval": false, - "IsTrustedTypesEval": false, - "IsUnsafeHashes": false, - "StrictDynamic": false, - "UnsafeHashedAttributes": false, - "UnsafeAllowRedirects": false, - "InlineSpeculationRules": false, - "Sources": [ - ], - "Nonces": [ - ], - "Hashes": [ - ], - "RequireTrustedTypes": false, - "RequireSri": false, - "ReportSample": true - }, - "Styles": { - "IsNone": false, - "IsSelf": true, - "IsUnsafeInline": false, - "IsUnsafeHashes": false, - "Sources": [ - ], - "Nonces": [ - ], - "Hashes": [ - ], - "RequireSri": false, - "ReportSample": true - }, - "PermissionsPolicy": { - "Gamepad": { - "IsNone": false, - "IsSelf": true, - "Sources": [ - ] - } - }, - "ReportTo": { - "Group": "csp-reports", - "MaxAge": "60", - "Endpoints": [ - ] - } - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/wwwroot/csp-violation.html b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/wwwroot/csp-violation.html deleted file mode 100644 index 0f8f47aa..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/wwwroot/csp-violation.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - CSP Violation Test - - -

CSP Violation Test

- -

This page will trigger a CSP violation by loading an external script and by using eval.

- - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile b/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile deleted file mode 100644 index 0bcd6f21..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentSecurityPolicy.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/LICENSE b/Api.PolicyHeaders.ContentSecurityPolicy/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/README.md b/Api.PolicyHeaders.ContentSecurityPolicy/README.md deleted file mode 100644 index 4b49d6fc..00000000 --- a/Api.PolicyHeaders.ContentSecurityPolicy/README.md +++ /dev/null @@ -1,130 +0,0 @@ -# Api.PolicyHeaders.ContentSecurityPolicy - -> _Nano API application with content security policy (CSP)._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Hosting.Https](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Hosting.Https)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -Content Security Policy (CSP) supports a wide range of configurations. This example demonstrates a small, representative subset of directives to illustrate -how CSP is configured and applied. You are encouraged to experiment with the settings and inspect the resulting `Content-Security-Policy` response header -to better understand their effects. - -The service is configured to run over HTTPS, as most browsers will not send CSP violation reports to a `csp-report` endpoint over HTTP. -The `Report-To` directive is enabled and configured to send reports to Nano’s default endpoint at `/csp/report-to`. - -> ⚠️ Browsers typically batch and delay CSP reports, so it may take up to a minute—or longer—before reports are sent. - -To observe CSP violations in action, load the provided `csp-violation.html` file and inspect the browser’s reporting behavior. - -| Endpoint | Description | -| ---------------------------------------- | --------------------------------------------------------------- | -| `http://localhost:8080/api/examples/csp` | Returns a `200 OK` response including the CSP response header. | - -> 📖 Learn more about **[Nano Content Security Options](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#content-type-options)**. - -## Configuration -Added content security policy header configuration. - -```json -"App": { - "HttpPolicyHeaders": { - "Csp": { - "ReportOnly": false, - "UpgradeInsecureRequests": true, - "Defaults": { - "IsNone": false, - "IsSelf": true, - "Sources": [ - "https://localhost" - ] - }, - "Scripts": { - "IsNone": false, - "IsSelf": true, - "IsUnsafeInline": false, - "IsUnsafeEval": false, - "IsUnsafeWasmEval": false, - "IsTrustedTypesEval": false, - "IsUnsafeHashes": false, - "StrictDynamic": false, - "UnsafeHashedAttributes": false, - "UnsafeAllowRedirects": false, - "InlineSpeculationRules": false, - "Sources": [ - ], - "Nonces": [ - ], - "Hashes": [ - ], - "RequireTrustedTypes": false, - "RequireSri": false, - "ReportSample": true - }, - "Styles": { - "IsNone": false, - "IsSelf": true, - "IsUnsafeInline": false, - "IsUnsafeHashes": false, - "Sources": [ - ], - "Nonces": [ - ], - "Hashes": [ - ], - "RequireSri": false, - "ReportSample": true - }, - "StylesElem": { - "IsNone": false, - "IsSelf": true, - "IsUnsafeInline": false, - "Sources": [ - ], - "Nonces": [ - ], - "Hashes": [ - ], - "ReportSample": true - }, - "StylesAttr": { - "IsNone": false, - "IsSelf": true, - "IsUnsafeInline": false, - "IsUnsafeHashes": false, - "Sources": [ - ], - "ReportSample": true - }, - "PermissionsPolicy": { - "Gamepad": { - "IsNone": false, - "IsSelf": true, - "Sources": [ - ] - } - }, - "ReportTo": { - "Group": "csp-reports", - "MaxAge": "60", - "Endpoints": [ - ] - } - } - } -} -``` diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/icon.png b/Api.PolicyHeaders.ContentSecurityPolicy/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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
?}-`UAua z$|bV$LW!(!2F-yX!hrvgm^PtArZo(aX_-+}7~9`d?5rSgE)jZwAwp>w0T}ClX@>bx z=nc*HCI7IQg7EC^xSi` zsh$H1d#kBqFmjWWI+-2xxn^#;s{h@=q|gCJluh!Pw-eF_rONGd>@q%yRn;^mZCTyH z*26kLB~H(0Gltgp(KQ$%ReX5$MPV|ll+XnthXCJTu5OoY{BHBm;#hnsKKBV=>#UW( zw9u6%l~Y{zB6m1YW!#!8NH}K$HJ#LP56kX~i&#nl;g4?eTu|T5!LHTwh8tY=Fs`VJ zb?Kxr?SFiU}Y`8lk86mUVIXpbkK>-+JC=Sxm5NdCHSUJB9?q^`1W$(T{e%-snnLx zO5KFchJfr5BjyAhN`p*`Cn}tcx?F8dLPCt^q(5s31yMEkbPEIK@Lw&ir>&Z2`q-wl zsD#)0*SO7b`fch(ik40~$PF0sZ8ORKn)g1o-Cm@#iT>(QQpJ5KFUXRg^x#G#*X6|m<`(ti3*x=1Irq*+c~4co zhg&IdsQbaA#xqvwW4!hQBgM~LU$Mz?+!FlDw0~TE-vl|>QEfu9#B}lVh}7Jtn*>s8 zUite2{w1sx$`Sn77XkblNLmyO>9Wg`cG$9a@=J@_yYq+=3~Vc3i@hh zyOd^KWC)u`e}#0o-@~g9aB*BGD>6=28;^pqkU~RdyL49)8clm_LR$9`H(*<`1hj^puHQE**9*~Rr zP7F<6xLme0%N@}V9e-&zZGAGMquzRzj@s6Nq>e1o^dBIGM)LK|1-mK ziIDFYB4h^)0vN~IzY6I8gMYV&N7=G*zUNo)-zP#^9qzR+c}J`)@L93Ef)2ggDdaPt2vGBzc#BZji$}jwpm#Q9fhD0G_ zc^f?)UD%4YpDolwcijh{oBf$9SdsXb?!|Vh94_d?1huw78#Qy9{q$8kj*n5)Gx-y~ zI>u&v8yPirA>_{8I5{~*nAv1eYvW=%WuTr?4qJvLq({*o%UocV@azU zaj#1h@79FpSMQXeUpIR@rrR-({nWx~rg713#k}io_ci}izM<*Msjx9|Wc+%AWJ`o9 zCor5Ft`j@hX0kn!C|SDwz0Rdn@fJZ5n0j{#;Ov89Y|K7Z)K;D&e%IjJ6hgscs2mU? z4lcMUldRVglWq#X|1w*_)hYf|(T2tMJbFA@0mWl2g7p+`E)%Qt+QMGrU(TW{i4GV{ zNuq$7Akx)fe%(L%QL?OOyk;cW>C<14EelmK%;(%Z^Y;dV%TVeb`gqT~v}Vh<8VfNT zuD%zUiKq4)ES6q{KP>u@xhSACOzh0Um_n5r^mf!b;NRp@BCEsJ?+91qD)st+j%660 z(1BQUcja)n>L?u&njV8?3}i}NZcIL;q?$2^hxIc9M4wfP>JFDr~B zlFM12@c=`XoS`I#BlPcoXX+O92^}>IGF4_@x4g58cDh(M_wJ8Yf>Zt7hj5kx*M4np zBYPGkvA7R+9y~ct2aFPL6XY*=IBiHxZM9E#bC{=@y1B-^?nT{R>SICc_Pa^H9yXaN zx^)GLrI8cGy&4C+lS4OXyApuLe%%)A)e z;_kiFYvU@$P1zo6JC#xI*t00gYZ_`A(3PUlCvjpo!mOJQ`ttv(TLjkw88@WD8J zGXOvp&EDt*dGwraDSfD?Y-7!8aNh8f0$Itl^vLlKjgUEXXr83t@WQ$-mF8yZgtQ%j+U>*9qY?V|RtA{s6P QtUG7zSmhZ)eg(jP05{mZ`~Uy| diff --git a/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.dcproj b/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.dcproj deleted file mode 100644 index d9e8e500..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.dcproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.yml b/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.yml deleted file mode 100644 index a88b70bc..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.policyheaders.contenttypeoptions: - image: api.policyheaders.contenttypeoptions - hostname: api-policyheaders-contenttypeoptions - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.PolicyHeaders.ContentTypeOptions - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.dockerignore b/Api.PolicyHeaders.ContentTypeOptions/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.ContentTypeOptions/.github/config/slack.yml b/Api.PolicyHeaders.ContentTypeOptions/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 1bd59324..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.PolicyHeaders.ContentTypeOptions - IMAGE_NAME: api.policyheaders.contenttypeoptions - SERVICE_NAME: api-policyheaders-contenttypeoptions - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.gitignore b/Api.PolicyHeaders.ContentTypeOptions/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/service.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Tests.Api.PolicyHeaders.ContentTypeOptions.csproj b/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Tests.Api.PolicyHeaders.ContentTypeOptions.csproj deleted file mode 100644 index 271fed39..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Tests.Api.PolicyHeaders.ContentTypeOptions.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj deleted file mode 100644 index 890be2f1..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.sln b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.sln deleted file mode 100644 index 69f63482..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.sln +++ /dev/null @@ -1,134 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ContentTypeOptions.Models", "Api.PolicyHeaders.ContentTypeOptions.Models\Api.PolicyHeaders.ContentTypeOptions.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ContentTypeOptions", "Api.PolicyHeaders.ContentTypeOptions\Api.PolicyHeaders.ContentTypeOptions.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.ContentTypeOptions", ".tests\Tests.Api.PolicyHeaders.ContentTypeOptions\Tests.Api.PolicyHeaders.ContentTypeOptions.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.csproj b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.csproj deleted file mode 100644 index 971bbf26..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Controllers/ExamplesController.cs b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Controllers/ExamplesController.cs deleted file mode 100644 index 8e3de49e..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.PolicyHeaders.ContentTypeOptions.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Content Type Options Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("nosniff")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task ContentTypeOptionsAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("nosniff"); - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local deleted file mode 100644 index c7050317..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentTypeOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Program.cs b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 1013f4a7..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.ContentTypeOptions")] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Development.json b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Production.json b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Staging.json b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.json b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.json deleted file mode 100644 index 82bc5f42..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HttpPolicyHeaders": { - "ContentType": { - "NoSniff": true - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation-wrong-type.txt b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation-wrong-type.txt deleted file mode 100644 index 22c276ec..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation-wrong-type.txt +++ /dev/null @@ -1 +0,0 @@ -console.log("This should NOT execute if content-type nosniff is enabled!"); \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation.html b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation.html deleted file mode 100644 index f21353e5..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - Content-Type Nosniff Test - - -

Content-Type Nosniff Test

- -

This page will attempt to load a script served with the wrong Content-Type.

- - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Dockerfile b/Api.PolicyHeaders.ContentTypeOptions/Dockerfile deleted file mode 100644 index fddba199..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentTypeOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/LICENSE b/Api.PolicyHeaders.ContentTypeOptions/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/README.md b/Api.PolicyHeaders.ContentTypeOptions/README.md deleted file mode 100644 index 7d5bc816..00000000 --- a/Api.PolicyHeaders.ContentTypeOptions/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Api.PolicyHeaders.ContentTypeOptions - -> _Nano API application with content type options._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -To observe content-type sniffing in action, load the `content-type-sniff-violation.html` file and see the browser block execution of the script -served with an incorrect `.txt` content-type. - -| Endpoint | Description | -| -------------------------------------------- | ---------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/nosniff` | Returns a `200 OK` response including the Content-Type `nosniff` response header. | - -> 📖 Learn more about **[Nano Content Type Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#content-type-options)**. - -## Configuration -```json -"App": { - "HttpPolicyHeaders": { - "ContentType": { - "NoSniff": true - } - } -} -``` diff --git a/Api.PolicyHeaders.ContentTypeOptions/icon.png b/Api.PolicyHeaders.ContentTypeOptions/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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
- - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.docker/docker-compose.yml b/Api.PolicyHeaders.Cors/.docker/docker-compose.yml deleted file mode 100644 index 78322a2c..00000000 --- a/Api.PolicyHeaders.Cors/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.policyheaders.cors: - image: api.policyheaders.cors - hostname: api-policyheaders-cors - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.PolicyHeaders.Cors - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.dockerignore b/Api.PolicyHeaders.Cors/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.PolicyHeaders.Cors/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.Cors/.github/config/slack.yml b/Api.PolicyHeaders.Cors/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.PolicyHeaders.Cors/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml deleted file mode 100644 index b3a31729..00000000 --- a/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.PolicyHeaders.Cors - IMAGE_NAME: api.policyheaders.cors - SERVICE_NAME: api-policyheaders-cors - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.gitignore b/Api.PolicyHeaders.Cors/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.PolicyHeaders.Cors/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.Cors/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.PolicyHeaders.Cors/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.Cors/.kubernetes/service.yaml b/Api.PolicyHeaders.Cors/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.PolicyHeaders.Cors/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Tests.Api.PolicyHeaders.Cors.csproj b/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Tests.Api.PolicyHeaders.Cors.csproj deleted file mode 100644 index 45848230..00000000 --- a/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Tests.Api.PolicyHeaders.Cors.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj deleted file mode 100644 index 890be2f1..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.sln b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.sln deleted file mode 100644 index 06c264a5..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.sln +++ /dev/null @@ -1,134 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Cors.Models", "Api.PolicyHeaders.Cors.Models\Api.PolicyHeaders.Cors.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Cors", "Api.PolicyHeaders.Cors\Api.PolicyHeaders.Cors.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.Cors", ".tests\Tests.Api.PolicyHeaders.Cors\Tests.Api.PolicyHeaders.Cors.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.csproj b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.csproj deleted file mode 100644 index 4959095d..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Controllers/ExamplesController.cs b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Controllers/ExamplesController.cs deleted file mode 100644 index 1b11b762..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.PolicyHeaders.Cors.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Cors Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("cors")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task CorsAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("cors"); - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local deleted file mode 100644 index 27de0430..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Cors.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Program.cs b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Properties/InternalsVisibleTo.cs deleted file mode 100644 index b047e048..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.Cors")] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Development.json b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Production.json b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Staging.json b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.json b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.json deleted file mode 100644 index 5e89c5f7..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HttpPolicyHeaders": { - "Cors": { - "AllowedOrigins": [ - "http://localhost:8080" - ], - "AllowedHeaders": [ - "Content-Type" - ], - "AllowedMethods": [ - "GET", - "POST", - "OPTIONS" - ], - "AllowCredentials": false, - "Origin": { - "EmbedderPolicy": "UnsafeNone", - "OpenerPolicy": "SameOriginAllowPopups", - "ResourcePolicy": "SameOrigin" - }, - "ExposedHeaders": [ - ] - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-credetntials-not-alloweed.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-credetntials-not-alloweed.html deleted file mode 100644 index 9826d21b..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-credetntials-not-alloweed.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - CORS Credentials Violation - - -

CORS Credentials Violation

- - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-header-not-allowed.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-header-not-allowed.html deleted file mode 100644 index 4ae956d6..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-header-not-allowed.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CORS Header Violation - - -

CORS Header Violation

- - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-method-not-allowed.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-method-not-allowed.html deleted file mode 100644 index 9889796c..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-method-not-allowed.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - CORS Method Violation - - -

CORS Method Violation

- - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-origin-not-allowed.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-origin-not-allowed.html deleted file mode 100644 index f2fbc8fa..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-origin-not-allowed.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - CORS Origin Violation - - -

CORS Origin Violation

- - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-preflight-blocked.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-preflight-blocked.html deleted file mode 100644 index 247c4312..00000000 --- a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-preflight-blocked.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - CORS Preflight Violation - - -

CORS Preflight Violation

- - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Dockerfile b/Api.PolicyHeaders.Cors/Dockerfile deleted file mode 100644 index 1c4803ef..00000000 --- a/Api.PolicyHeaders.Cors/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Cors.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/LICENSE b/Api.PolicyHeaders.Cors/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.PolicyHeaders.Cors/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/README.md b/Api.PolicyHeaders.Cors/README.md deleted file mode 100644 index d9c20aa9..00000000 --- a/Api.PolicyHeaders.Cors/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Api.PolicyHeaders.Cors - -> _Nano API application with cors._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -To test CORS behavior, open the provided HTML pages and observe how the browser enforces different CORS restrictions and blocks unauthorized requests. - -Also try out the endpoint, and observe how CORS returns the allowed hosts, headers, and methods. - -| Endpoint | Description | -| ----------------------------------------- | ----------------------------------------------------------------- | -| `http://localhost:8080/api/examples/cors` | Returns a `200 OK` response including the CORS response headers. | - -> 📖 Learn more about **[Nano Cors](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#cors)**. - -## Configuration -```json -"Cors": { - "AllowedOrigins": [ - "http://localhost:8080" - ], - "AllowedHeaders": [ - "Content-Type" - ], - "AllowedMethods": [ - "GET", - "POST", - "OPTIONS" - ], - "AllowCredentials": true, - "Origin": { - "EmbedderPolicy": "UnsafeNone", - "OpenerPolicy": "SameOriginAllowPopups", - "ResourcePolicy": "SameOrigin" - } -} -``` diff --git a/Api.PolicyHeaders.Cors/icon.png b/Api.PolicyHeaders.Cors/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~
- - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.yml b/Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.yml deleted file mode 100644 index c0253a40..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.policyheaders.forwardedheaders: - image: api.policyheaders.forwardedheaders - hostname: api-policyheaders-forwardedheaders - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.PolicyHeaders.ForwardedHeaders - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.dockerignore b/Api.PolicyHeaders.ForwardedHeaders/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.ForwardedHeaders/.github/config/slack.yml b/Api.PolicyHeaders.ForwardedHeaders/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml deleted file mode 100644 index d8c63ecc..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.PolicyHeaders.ForwardedHeaders - IMAGE_NAME: api.policyheaders.forwardedheaders - SERVICE_NAME: api-policyheaders-forwardedheaders - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.gitignore b/Api.PolicyHeaders.ForwardedHeaders/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/service.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Tests.Api.PolicyHeaders.ForwardedHeaders.csproj b/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Tests.Api.PolicyHeaders.ForwardedHeaders.csproj deleted file mode 100644 index 13dbdb0e..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Tests.Api.PolicyHeaders.ForwardedHeaders.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj deleted file mode 100644 index 890be2f1..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.sln b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.sln deleted file mode 100644 index b0532572..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.sln +++ /dev/null @@ -1,134 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ForwardedHeaders.Models", "Api.PolicyHeaders.ForwardedHeaders.Models\Api.PolicyHeaders.ForwardedHeaders.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ForwardedHeaders", "Api.PolicyHeaders.ForwardedHeaders\Api.PolicyHeaders.ForwardedHeaders.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.ForwardedHeaders", ".tests\Tests.Api.PolicyHeaders.ForwardedHeaders\Tests.Api.PolicyHeaders.ForwardedHeaders.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.csproj b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.csproj deleted file mode 100644 index 7f7bca61..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Controllers/ExamplesController.cs b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Controllers/ExamplesController.cs deleted file mode 100644 index 027ef17e..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Controllers/ExamplesController.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.PolicyHeaders.ForwardedHeaders.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Forwarded Headers Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("forwarded-headers")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task ForwardedHeadersAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return Ok(new - { - RemoteIp = HttpContext.Connection.RemoteIpAddress?.ToString(), - HttpContext.Request.Scheme, - Host = HttpContext.Request.Host.Value, - OriginalHeaders = HttpContext.Request.Headers - .Where(x => x.Key.StartsWith("X-Original")) - .ToDictionary(x => x.Key, x => x.Value.ToString()) - }); - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local deleted file mode 100644 index dcca3656..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ForwardedHeaders.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Program.cs b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 61df2b28..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.ForwardedHeaders")] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Development.json b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Production.json b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Staging.json b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.json b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.json deleted file mode 100644 index e9f5b5fc..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HttpPolicyHeaders": { - "ForwardedHeaders": { - "Headers": "All", - "RequireHeaderSymmetry": true - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Dockerfile b/Api.PolicyHeaders.ForwardedHeaders/Dockerfile deleted file mode 100644 index 2efeaed7..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ForwardedHeaders.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/LICENSE b/Api.PolicyHeaders.ForwardedHeaders/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/README.md b/Api.PolicyHeaders.ForwardedHeaders/README.md deleted file mode 100644 index b39ca72b..00000000 --- a/Api.PolicyHeaders.ForwardedHeaders/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Api.PolicyHeaders.ForwardedHeaders - -> _Nano API application with forwarded headers enabled._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -Invoke the endpoint in the example controller to see the updated scheme, host, and remote IP address. The response also includes the original headers, -which reflect the internal service values rather than the forwarded ones. The example request includes three `X-Forwarded-*` headers to simulate a reverse proxy scenario. - -| Endpoint | Description | -| -------------------------------------------- | ------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/nosniff` | Returns a `200 OK` response with `HttpContext` forwarded header values. | - -> 📖 Learn more about **[Nano Forwarded Headers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#forwarded-headers)**. - -## Configuration -```json -"App": { - "HttpPolicyHeaders": { - "ForwardedHeaders": { - "Headers": "All", - "RequireHeaderSymmetry": true - } - } -} -``` diff --git a/Api.PolicyHeaders.ForwardedHeaders/icon.png b/Api.PolicyHeaders.ForwardedHeaders/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.docker/docker-compose.yml b/Api.PolicyHeaders.FrameOptions/.docker/docker-compose.yml deleted file mode 100644 index 641fe30c..00000000 --- a/Api.PolicyHeaders.FrameOptions/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.policyheaders.frameoptions: - image: api.policyheaders.frameoptions - hostname: api-policyheaders-frameoptions - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.PolicyHeaders.FrameOptions - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.dockerignore b/Api.PolicyHeaders.FrameOptions/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.PolicyHeaders.FrameOptions/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.FrameOptions/.github/config/slack.yml b/Api.PolicyHeaders.FrameOptions/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.PolicyHeaders.FrameOptions/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml deleted file mode 100644 index a6d34287..00000000 --- a/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.PolicyHeaders.FrameOptions - IMAGE_NAME: api.policyheaders.frameoptions - SERVICE_NAME: api-policyheaders-frameoptions - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.gitignore b/Api.PolicyHeaders.FrameOptions/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.PolicyHeaders.FrameOptions/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.PolicyHeaders.FrameOptions/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/service.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.PolicyHeaders.FrameOptions/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Tests.Api.PolicyHeaders.FrameOptions.csproj b/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Tests.Api.PolicyHeaders.FrameOptions.csproj deleted file mode 100644 index 060b06ae..00000000 --- a/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Tests.Api.PolicyHeaders.FrameOptions.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj deleted file mode 100644 index 890be2f1..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.sln b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.sln deleted file mode 100644 index e7325555..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.sln +++ /dev/null @@ -1,134 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.FrameOptions.Models", "Api.PolicyHeaders.FrameOptions.Models\Api.PolicyHeaders.FrameOptions.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.FrameOptions", "Api.PolicyHeaders.FrameOptions\Api.PolicyHeaders.FrameOptions.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.FrameOptions", ".tests\Tests.Api.PolicyHeaders.FrameOptions\Tests.Api.PolicyHeaders.FrameOptions.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.csproj b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.csproj deleted file mode 100644 index f9a4cf22..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Controllers/ExamplesController.cs b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Controllers/ExamplesController.cs deleted file mode 100644 index 10a7c1ab..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.PolicyHeaders.FrameOptions.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Nosniff Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("frameoptions")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task FrameOptionsAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("frameoptions"); - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local deleted file mode 100644 index bac6d4bb..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.FrameOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Program.cs b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 7edcb0e2..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.FrameOptions")] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Development.json b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Production.json b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Staging.json b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.json b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.json deleted file mode 100644 index 5d172fb4..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HttpPolicyHeaders": { - "FrameOptions": { - "FrameOptionsPolicyHeader": "SameOrigin" - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/wwwroot/frame-options-violation.html b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/wwwroot/frame-options-violation.html deleted file mode 100644 index 3e3a7e2d..00000000 --- a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/wwwroot/frame-options-violation.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - X-Frame-Options SAMEORIGIN Violation - - - -

X-Frame-Options: SAMEORIGIN Test

- -

This page attempts to embed a page protected with X-Frame-Options: SAMEORIGIN from a different origin.

- - - -

Expected result: the iframe is blocked and remains blank. Check the browser console for a violation message.

- - \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Dockerfile b/Api.PolicyHeaders.FrameOptions/Dockerfile deleted file mode 100644 index dae0c7d9..00000000 --- a/Api.PolicyHeaders.FrameOptions/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.FrameOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/LICENSE b/Api.PolicyHeaders.FrameOptions/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.PolicyHeaders.FrameOptions/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/README.md b/Api.PolicyHeaders.FrameOptions/README.md deleted file mode 100644 index 487fe6b4..00000000 --- a/Api.PolicyHeaders.FrameOptions/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Api.PolicyHeaders.FrameOptions - -> _Nano API application with frame options._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -To observe X-Frame-Options enforcement, load the `frame-options-violation.html` file and see the browser block the page from being embedded in an iframe. - -| Endpoint | Description | -| ------------------------------------------------- | ----------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/frameoptions` | Returns a `200 OK` response including the `X-Frame-Options` response header. | - -> 📖 Learn more about **[Nano Frame Options Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#frame-options)**. - -## Configuration -```json -"App": { - "HttpPolicyHeaders": { - "FrameOptions": { - "FrameOptionsPolicyHeader": "SameOrigin" - } - } -} -``` \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/icon.png b/Api.PolicyHeaders.FrameOptions/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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
- - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.docker/docker-compose.yml b/Api.PolicyHeaders.Hsts/.docker/docker-compose.yml deleted file mode 100644 index 0c46f0dd..00000000 --- a/Api.PolicyHeaders.Hsts/.docker/docker-compose.yml +++ /dev/null @@ -1,20 +0,0 @@ -services: - api.policyheaders.hsts: - image: api.policyheaders.hsts - hostname: api-policyheaders-hsts - restart: on-failure - ports: - - 8080:8080 - - 4443:4443 - build: - context: ../Api.PolicyHeaders.Hsts - dockerfile: "Dockerfile.Local" - volumes: - - ../:/root/.dotnet/https - networks: - - network - -networks: - network: - name: network - driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.dockerignore b/Api.PolicyHeaders.Hsts/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.PolicyHeaders.Hsts/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.Hsts/.github/config/slack.yml b/Api.PolicyHeaders.Hsts/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.PolicyHeaders.Hsts/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml deleted file mode 100644 index d73d6dc5..00000000 --- a/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,188 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.PolicyHeaders.Hsts - IMAGE_NAME: api.policyheaders.hsts - SERVICE_NAME: api-policyheaders-hsts - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - CERTIFICATE_ISSUER: letsencrypt-prod - CERTIFICATE_ORGANIZATION: ${{ vars.CERTIFICATE_ORGANIZATION }} - CERTIFICATE_HOST: ${{ github.ref == 'refs/heads/master' && vars.HOST_API_SUBDOMAIN + '.' + vars.PRODUCTION_HOST || vars.HOST_API_SUBDOMAIN + '.' + vars.STAGING_HOST }} - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/certificate.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/certificate.tmp.yaml; - sudo kubectl apply -f .kubernetes/certificate.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/ingress.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/ingress.tmp.yaml; - sudo kubectl apply -f .kubernetes/ingress.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.gitignore b/Api.PolicyHeaders.Hsts/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.PolicyHeaders.Hsts/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.PolicyHeaders.Hsts/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/certificate.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/certificate.yaml deleted file mode 100644 index a37bdadc..00000000 --- a/Api.PolicyHeaders.Hsts/.kubernetes/certificate.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: %SERVICE_NAME%-nginx-tls - namespace: %KUBERNETES_NAMESPACE% -spec: - secretName: %CERTIFICATE_HOST%-tls - duration: 2160h - renewBefore: 720h - subject: - organizations: - - %CERTIFICATE_ORGANIZATION% - dnsNames: - - %CERTIFICATE_HOST% - privateKey: - rotationPolicy: Always - issuerRef: - name: %CERTIFICATE_ISSUER% - kind: ClusterIssuer diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/ingress.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/ingress.yaml deleted file mode 100644 index 0acc7163..00000000 --- a/Api.PolicyHeaders.Hsts/.kubernetes/ingress.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ingress-%SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ingressClassName: nginx - tls: - - hosts: - - %CERTIFICATE_HOST% - secretName: %CERTIFICATE_HOST%-tls - rules: - - host: %CERTIFICATE_HOST% - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: %SERVICE_NAME% - port: - number: 8080 diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/service.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.PolicyHeaders.Hsts/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Tests.Api.PolicyHeaders.Hsts.csproj b/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Tests.Api.PolicyHeaders.Hsts.csproj deleted file mode 100644 index 51e93680..00000000 --- a/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Tests.Api.PolicyHeaders.Hsts.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj deleted file mode 100644 index 890be2f1..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.sln b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.sln deleted file mode 100644 index 4ff64a43..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - localhost.pfx = localhost.pfx - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\certificate.yaml = .kubernetes\certificate.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\ingress.yaml = .kubernetes\ingress.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Hsts.Models", "Api.PolicyHeaders.Hsts.Models\Api.PolicyHeaders.Hsts.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Hsts", "Api.PolicyHeaders.Hsts\Api.PolicyHeaders.Hsts.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.Hsts", ".tests\Tests.Api.PolicyHeaders.Hsts\Tests.Api.PolicyHeaders.Hsts.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.csproj b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.csproj deleted file mode 100644 index 7db249e4..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Controllers/ExamplesController.cs b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Controllers/ExamplesController.cs deleted file mode 100644 index 3613c10d..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.PolicyHeaders.Hsts.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Hsts Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("hsts")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task HstsAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("hsts"); - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local deleted file mode 100644 index 86fa8383..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 4443 - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Hsts.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Program.cs b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 671ab2b3..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.Hsts")] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Development.json b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Development.json deleted file mode 100644 index 8da7c4bf..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Development.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "App": { - "Hosting": { - "Https": { - "Ports": [ - 4443 - ], - "Certificate": { - "Path": "/root/.dotnet/https/localhost.pfx", - "Password": "password" - } - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Production.json b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Staging.json b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.json b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.json deleted file mode 100644 index 1f00e26e..00000000 --- a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HttpPolicyHeaders": { - "Hsts": { - "MaxAge": "00:01:00", - "UsePreload": false, - "IncludeSubdomains": true - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/wwwroot/hsts-violation-image.png b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/wwwroot/hsts-violation-image.png deleted file mode 100644 index 1d79719a50bcfb9e5373641c1b98613c12b0b03a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15262 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 - - - - HSTS Test on Localhost - - -

HSTS Test on Localhost

- -

This page demonstrates HTTP Strict Transport Security (HSTS) behavior. HSTS instructs browsers to always use HTTPS for a given host.

- -

- Important notes for localhost: -

    -
  • Browsers normally exclude localhost from automatic HSTS upgrades to avoid dev lock-in.
  • -
  • Because we cannot bind HTTP to the same port as HTTPS, we cannot fully test automatic HTTP → HTTPS upgrade on localhost.
  • -
  • We can still verify that the Strict-Transport-Security header is being sent and cached by the browser.
  • -
-

- -

- Observed behavior in this page: -

    -
  • All resources use HTTPS to satisfy HSTS.
  • -
  • Direct HTTP requests on other ports (e.g., port 8080) will not be upgraded, because HSTS is port-specific.
  • -
  • Mixed-content blocking is enforced normally by the browser.
  • -
-

- - -
- - - - - - HSTS test image - -

Note: Automatic upgrade from HTTP → HTTPS cannot be demonstrated on localhost because the HTTP port differs from the HTTPS port. This behavior works on real hosts in production.

- - \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Dockerfile b/Api.PolicyHeaders.Hsts/Dockerfile deleted file mode 100644 index 477de8f6..00000000 --- a/Api.PolicyHeaders.Hsts/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Hsts.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/LICENSE b/Api.PolicyHeaders.Hsts/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.PolicyHeaders.Hsts/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/README.md b/Api.PolicyHeaders.Hsts/README.md deleted file mode 100644 index 47f8c078..00000000 --- a/Api.PolicyHeaders.Hsts/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Api.PolicyHeaders.Hsts - -> _Nano API application with strict-transport-security (HSTS)._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Hosting.Https](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Hosting.Https)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -The service is configured to run over HTTPS, since HSTS requires a secure connection to be tested effectively. -To observe HSTS enforcement in action, load the `hsts-violation.html` file and see how the browser blocks non-secure requests. - -| Endpoint | Description | -| ----------------------------------------- | --------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/hsts` | Returns a `200 OK` response including the `Strict-Transform-Security` response header. | - -> 📖 Learn more about **[Nano Strict Transport Security (HSTS)](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#strict-transport-security-hsts)**. - -## Configuration -```json -"App": { - "HttpPolicyHeaders": { - "Hsts": { - "MaxAge": "00:01:00", - "UsePreload": false, - "IncludeSubdomains": true - } - } -} -``` diff --git a/Api.PolicyHeaders.Hsts/icon.png b/Api.PolicyHeaders.Hsts/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 QtUG7zSmhZ)eg(jP05{mZ`~Uy| diff --git a/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.dcproj b/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.dcproj deleted file mode 100644 index d9e8e500..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.dcproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.yml b/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.yml deleted file mode 100644 index a3364eab..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.policyheaders.referrerpolicy: - image: api.policyheaders.referrerpolicy - hostname: api-policyheaders-referrerpolicy - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.PolicyHeaders.ReferrerPolicy - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.dockerignore b/Api.PolicyHeaders.ReferrerPolicy/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.ReferrerPolicy/.github/config/slack.yml b/Api.PolicyHeaders.ReferrerPolicy/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 0cc47f35..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.PolicyHeaders.ReferrerPolicy - IMAGE_NAME: api.policyheaders.referrerpolicy - SERVICE_NAME: api-policyheaders-referrerpolicy - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.gitignore b/Api.PolicyHeaders.ReferrerPolicy/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/service.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Tests.Api.PolicyHeaders.ReferrerPolicy.csproj b/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Tests.Api.PolicyHeaders.ReferrerPolicy.csproj deleted file mode 100644 index a8eb9bd7..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Tests.Api.PolicyHeaders.ReferrerPolicy.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj deleted file mode 100644 index 890be2f1..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.sln b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.sln deleted file mode 100644 index 1f1a70d8..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.sln +++ /dev/null @@ -1,134 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ReferrerPolicy.Models", "Api.PolicyHeaders.ReferrerPolicy.Models\Api.PolicyHeaders.ReferrerPolicy.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ReferrerPolicy", "Api.PolicyHeaders.ReferrerPolicy\Api.PolicyHeaders.ReferrerPolicy.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.ReferrerPolicy", ".tests\Tests.Api.PolicyHeaders.ReferrerPolicy\Tests.Api.PolicyHeaders.ReferrerPolicy.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU - {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.csproj b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.csproj deleted file mode 100644 index b8c792a6..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Controllers/ExamplesController.cs b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Controllers/ExamplesController.cs deleted file mode 100644 index 82f02d8b..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.PolicyHeaders.ReferrerPolicy.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Referrer Policy Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("referrer-policy")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task ReferrerPolicyAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("referrer-policy"); - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local deleted file mode 100644 index fc184f16..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ReferrerPolicy.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Program.cs b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 9e08687b..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.ReferrerPolicy")] \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Development.json b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Production.json b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Staging.json b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.json b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.json deleted file mode 100644 index 6c782770..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HttpPolicyHeaders": { - "ReferrerPolicy": { - "ReferrerPolicyHeader": "SameOrigin" - } - } - } -} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/wwwroot/referrer-policy-violation.html b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/wwwroot/referrer-policy-violation.html deleted file mode 100644 index e9460934..00000000 --- a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/wwwroot/referrer-policy-violation.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - Referrer Policy Test - - -

Referrer Policy Test: same-origin

- -

- This page tests Referrer-Policy: same-origin. -

    -
  • Links or requests to the same origin will include the Referer header.
  • -
  • Links or requests to a different origin will not include the Referer.
  • -
-

- -

1. Same-origin link

-

- Click here (same origin) -

- -

2. Cross-origin link

-

- Click here (different origin) -

- -

3. Same-origin fetch

- -

-
-

4. Cross-origin fetch

- -

-
-
-
-
\ No newline at end of file
diff --git a/Api.PolicyHeaders.ReferrerPolicy/Dockerfile b/Api.PolicyHeaders.ReferrerPolicy/Dockerfile
deleted file mode 100644
index 48e82726..00000000
--- a/Api.PolicyHeaders.ReferrerPolicy/Dockerfile
+++ /dev/null
@@ -1,35 +0,0 @@
-ARG DOTNET_SDK_VERSION
-ARG DOTNET_ASPNET_VERSION
-ARG CONTAINER_REGISTRY_SOURCE_LABEL
-ARG NUGET_HOST
-ARG NUGET_USERNAME
-ARG NUGET_PASSWORD
-
-FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
-
-LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL
-
-EXPOSE 8080
-WORKDIR /app
-
-FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build
-ARG NUGET_HOST
-ARG NUGET_USERNAME
-ARG NUGET_PASSWORD
-
-WORKDIR /src
-COPY . .
-
-RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text
-RUN dotnet build -c Release -o /app
-
-FROM build AS publish
-RUN dotnet publish -c Release -o /app
-
-FROM base AS final
-WORKDIR /app
-COPY --from=publish /app .
-ENV COMPlus_EnableDiagnostics=0
-ENV DOTNET_USE_POLLING_FILE_WATCHER=true
-
-ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ReferrerPolicy.dll"]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.ReferrerPolicy/LICENSE b/Api.PolicyHeaders.ReferrerPolicy/LICENSE
deleted file mode 100644
index 006e8c21..00000000
--- a/Api.PolicyHeaders.ReferrerPolicy/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-The MIT License (MIT)
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
diff --git a/Api.PolicyHeaders.ReferrerPolicy/README.md b/Api.PolicyHeaders.ReferrerPolicy/README.md
deleted file mode 100644
index 59834f2e..00000000
--- a/Api.PolicyHeaders.ReferrerPolicy/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Api.PolicyHeaders.ReferrerPolicy
-
-> _Nano API application with referrer policy._  
-_All lessons are complete, self-contained examples that include build and deployment setup._
-
-> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. 
-Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._
-
-> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio.
-
-> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**.
-
-***
-
-## Table of Contents
-* [Summary](#summary)
-* [Configuration](#configuration)
-
-## Summary
-This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller 
-that inherits from the top-level Nano `BaseController`.  
-
-To observe referrer policy behavior in action, load the `referrer-policy-violation.html` file and inspect how the browser handles (or blocks) the referrer 
-in the resulting request.  
-
-| Endpoint                                             | Description                                                                                        |
-| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
-| `http://localhost:8080/api/examples/referrer-policy` | Returns a `200 OK` response including the `Referrer-Policy` response header set to `same-origin`.  |
-
-> 📖 Learn more about **[Nano Referrer Policy Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#referrer-policy)**.
-
-## Configuration
-```json
-"App": {
-  "HttpPolicyHeaders": {
-    "ReferrerPolicy": {
-      "ReferrerPolicyHeader": "SameOrigin"
-    }
-  }
-}
-```
diff --git a/Api.PolicyHeaders.ReferrerPolicy/icon.png b/Api.PolicyHeaders.ReferrerPolicy/icon.png
deleted file mode 100644
index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 14103
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
-
-  
-    2.1
-    Linux
-    false
-    http://localhost:{ServicePort}/docs
-    $(ProjectName)
-  
-  
-    
-  
-
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.docker/docker-compose.yml b/Api.PolicyHeaders.Robots/.docker/docker-compose.yml
deleted file mode 100644
index b45799e9..00000000
--- a/Api.PolicyHeaders.Robots/.docker/docker-compose.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-services:
-  api.policyheaders.robots:
-    image: api.policyheaders.robots
-    hostname: api-policyheaders-robots
-    restart: on-failure
-    ports:
-      - 8080:8080
-    build:
-      context: ../Api.PolicyHeaders.Robots
-      dockerfile: "Dockerfile.Local"
-    networks:
-      - network
-
-networks:
-  network:
-    name: network
-    driver: bridge
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.dockerignore b/Api.PolicyHeaders.Robots/.dockerignore
deleted file mode 100644
index e694ae21..00000000
--- a/Api.PolicyHeaders.Robots/.dockerignore
+++ /dev/null
@@ -1,12 +0,0 @@
-.dockerignore
-.env
-.git
-.gitignore
-.vs
-.vscode
-docker-compose.yml
-docker-compose.*.yml
-*/bin
-*/obj
-!obj/Docker/publish/*
-!obj/Docker/empty/
diff --git a/Api.PolicyHeaders.Robots/.github/config/slack.yml b/Api.PolicyHeaders.Robots/.github/config/slack.yml
deleted file mode 100644
index 3592affb..00000000
--- a/Api.PolicyHeaders.Robots/.github/config/slack.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-username: GitHub Actions
-icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png
-
-pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>."
-
-text: |
-  {{#if payload.commits}}
-  *Commits*
-  {{#each payload.commits}}
-  <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}}
-  {{/each}}
-  {{/if}}
-
-footer: >-
-  <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}}
-  
-fallback: |-
-  [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}}
diff --git a/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml
deleted file mode 100644
index 3c059778..00000000
--- a/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml
+++ /dev/null
@@ -1,171 +0,0 @@
-name: Build And Deploy
-on:
-  push
-env:
-  APP_NAME: Api.PolicyHeaders.Robots
-  IMAGE_NAME: api.policyheaders.robots
-  SERVICE_NAME: api-policyheaders-robots
-  VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}'
-  DOTNET_SDK_VERSION: 10.0
-  DOTNET_ASPNET_VERSION: 10.0
-  AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }}
-  AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
-  AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
-  AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
-  AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }}
-  NUGET_HOST: ${{ secrets.NUGET_HOST }}
-  NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }}
-  NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }}
-  NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }}
-  CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }}
-  CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
-  CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
-  CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }}
-  KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }}
-  KUBERNETES_NODEPOOL_COMPUTE: cpu
-  KUBERNETES_NAMESPACE: default
-  KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }}
-  KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }}
-  KUBERNETES_REPLICA_HISTORY_COUNT: 0
-  KUBERNETES_MEMORY_REQUEST: 512Mi   
-  KUBERNETES_MEMORY_LIMIT: 1536Mi
-  KUBERNETES_MEMORY_SCALING: 180
-  KUBERNETES_CPU_REQUEST: 200m
-  KUBERNETES_CPU_LIMIT: 600m
-  KUBERNETES_CPU_SCALING: 180
-  ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }}
-jobs:
-  build-and-deploy:
-    runs-on: ubuntu-latest
-    permissions:
-      contents: read
-      packages: write
-      id-token: write
-    concurrency:
-      group: ${{ github.repository }}
-      cancel-in-progress: true
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Azure Login
-        shell: pwsh
-        run: |
-          sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none;
-          sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none;
-          sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none;
-
-      - name: Build
-        shell: pwsh
-        run: |
-          dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text;
-
-          dotnet build -c Release .\$env:APP_NAME.sln;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-      - name: Test
-        shell: pwsh
-        run: |
-          dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj;
-          if ($LastExitCode -ne 0)
-          { 
-            throw "error";
-          };
-
-      - name: Publish Image
-        shell: pwsh
-        run: |
-          $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower()
-          $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest";
-          $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION
-          
-          sudo docker build `
-              -t $imageLatestTag `
-              -t $imageVersionTag `
-              --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION `
-              --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION `
-              --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL `
-              --build-arg NUGET_HOST=$env:NUGET_HOST `
-              --build-arg NUGET_USERNAME=$env:NUGET_USERNAME `
-              --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD `
-              ./;
-
-          if ($LastExitCode -ne 0) 
-          { 
-              throw "error";
-          };
-
-          sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST;
-          sudo docker push $imageLatestTag;
-          sudo docker push $imageVersionTag;
-          if ($LastExitCode -ne 0) 
-          { 
-              throw "error";
-          };  
-
-      - name: Publish NuGet
-        shell: pwsh
-        run: |
-          $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj";
-          dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build;
-          dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY;
-          if ($LastExitCode -ne 0) 
-          { 
-              throw "error";
-          };  
-
-      - name: Kubernetes Deploy
-        shell: pwsh
-        run: |
-         Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml;
-          sudo kubectl apply -f .kubernetes/service.tmp.yaml;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-          Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml;
-          sudo kubectl apply -f .kubernetes/configmap.tmp.yaml;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-          Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml;
-          sudo kubectl apply -f .kubernetes/deployment.tmp.yaml;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-          Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml;
-          sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-      - name: GitHub Release
-        if: github.ref == 'refs/heads/master'
-        uses: ncipollo/release-action@v1
-        with:
-          tag: v${{ env.VERSION }}
-          name: "Release ${{ env.VERSION }}"
-          body: |
-            Version: ${{ env.VERSION }}
-            Commit: ${{ github.sha }}
-          artifacts: "nupkgs/*"
-          token: ${{ secrets.GITHUB_TOKEN }}
-          draft: false
-          prerelease: false
-
-      - name: Slack Notification
-        if: always()
-        uses: act10ns/slack@v2
-        with:
-          webhook-url: ${{ secrets.SLACK_WEBHOOK }}
-          config: .github/config/slack.yml
-          status: ${{ job.status }}
-          channel: ${{ vars.SLACK_CHANNEL }}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.gitignore b/Api.PolicyHeaders.Robots/.gitignore
deleted file mode 100644
index 9921fc06..00000000
--- a/Api.PolicyHeaders.Robots/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-.vs
-*.user
-*.userprefs
-*.suo
-_ReSharper*
-**/bin
-**/obj
-*.DotSettings.User
-packages
-.env
-**/Properties/launchSettings.json
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.Robots/.kubernetes/autoscaler.yaml
deleted file mode 100644
index 95add8ad..00000000
--- a/Api.PolicyHeaders.Robots/.kubernetes/autoscaler.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-apiVersion: autoscaling/v2
-kind: HorizontalPodAutoscaler
-metadata:
-  name: %SERVICE_NAME%-hpa
-  namespace: %KUBERNETES_NAMESPACE%
-spec:
-  minReplicas: %KUBERNETES_REPLICA_COUNT%
-  maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX%
-  scaleTargetRef:
-    apiVersion: apps/v1
-    kind: Deployment
-    name: %SERVICE_NAME%
-  metrics:
-    - type: Resource
-      resource:
-        name: cpu
-        target:
-          type: Utilization
-          averageUtilization: %KUBERNETES_CPU_SCALING%
-    - type: Resource
-      resource:
-        name: memory
-        target:
-          type: Utilization
-          averageUtilization: %KUBERNETES_MEMORY_SCALING%
diff --git a/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml
deleted file mode 100644
index 3977c0ee..00000000
--- a/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-  apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: %SERVICE_NAME%-config
-  namespace: %KUBERNETES_NAMESPACE%
-data:
-  App__Version: %VERSION%
-  ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT%
diff --git a/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml
deleted file mode 100644
index fdc5dfdf..00000000
--- a/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml
+++ /dev/null
@@ -1,83 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: %SERVICE_NAME%
-  namespace: %KUBERNETES_NAMESPACE%
-  labels:
-    app: %SERVICE_NAME%
-spec:
-  replicas: %KUBERNETES_REPLICA_COUNT%
-  revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT%
-  selector:
-    matchLabels:
-      app: %SERVICE_NAME%
-  template:
-    metadata:
-      labels:
-        app: %SERVICE_NAME%
-    spec:
-      automountServiceAccountToken: false
-      securityContext:
-        runAsUser: 1000
-        runAsGroup: 2000 
-      topologySpreadConstraints:
-        - maxSkew: 1
-          topologyKey: kubernetes.io/hostname
-          whenUnsatisfiable: ScheduleAnyway
-          labelSelector:
-            matchLabels:
-              app: %SERVICE_NAME%  
-      automountServiceAccountToken: false
-      nodeSelector:
-        nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE%
-        kubernetes.io/os: linux
-      containers:
-      - name: %SERVICE_NAME%
-        image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION%
-        ports:
-        - containerPort: 8080
-        imagePullPolicy: Always 
-        envFrom:
-        - configMapRef:
-            name: %SERVICE_NAME%-config
-        resources:
-          requests:
-            memory: %KUBERNETES_MEMORY_REQUEST%
-            cpu: %KUBERNETES_CPU_REQUEST%
-          limits:
-            memory: %KUBERNETES_MEMORY_LIMIT%
-            cpu: %KUBERNETES_CPU_LIMIT%
-        securityContext:
-          privileged: false
-          allowPrivilegeEscalation: false
-          readOnlyRootFilesystem: true
-          runAsNonRoot: true
-          runAsUser: 1000
-          runAsGroup: 2000
-          capabilities:
-            drop:
-            - ALL
-        livenessProbe:
-          httpGet:
-            path: /healthz
-            port: 8080
-            scheme: HTTP
-          periodSeconds: 10
-          initialDelaySeconds: 30
-          timeoutSeconds: 2
-        readinessProbe:
-          httpGet:
-            path: /healthz
-            port: 8080
-            scheme: HTTP
-          periodSeconds: 5
-          initialDelaySeconds: 20
-          timeoutSeconds: 2
-        volumeMounts:
-        - name: tmp
-          mountPath: /tmp
-        - name: %SERVICE_NAME%-volume
-          mountPath: /mnt/%STORAGE_SHARE_NAME%
-      imagePullSecrets:
-        - name: ghcr-pull-secret
-
diff --git a/Api.PolicyHeaders.Robots/.kubernetes/service.yaml b/Api.PolicyHeaders.Robots/.kubernetes/service.yaml
deleted file mode 100644
index 2d8e20b2..00000000
--- a/Api.PolicyHeaders.Robots/.kubernetes/service.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  name: %SERVICE_NAME%
-  namespace: %KUBERNETES_NAMESPACE%
-spec:
-  ports:  
-  - name: http
-    port: 8080
-  selector:
-    app: %SERVICE_NAME%
-  type: ClusterIP
diff --git a/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Properties/DoNotParallelize.cs
deleted file mode 100644
index 6a076669..00000000
--- a/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Properties/DoNotParallelize.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-[assembly: DoNotParallelize]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Tests.Api.PolicyHeaders.Robots.csproj b/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Tests.Api.PolicyHeaders.Robots.csproj
deleted file mode 100644
index 6a6d6098..00000000
--- a/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Tests.Api.PolicyHeaders.Robots.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-	
-		net10.0
-		false
-		latest
-		
-		
-		true
-	
-
-	
-		
-		
-		
-	
-
-	
-	  
-	  
-	
-
-
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj
deleted file mode 100644
index 890be2f1..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-	
-		net10.0
-		
-		10.0.0.0
-		10.0.0.0
-		10.0.0.0
-		AnyCPU
-		Debug;Release
-		latest
-		en-US
-		enable
-		LICENSE
-		true
-		
-		True
-		true
-		true
-		true
-		Michael Vivet
-		Michael Vivet
-		$(ProjectName)
-		Example project demonstrating a focused aspect of the Nano Library.
-		
-			A practical example highlighting a specific area or scenario of Nano for learning
-			and experimentation. Shows how features or patterns can be applied without
-			representing a full application or complete reference.
-		
-		
-			- Added .NET 10 support
-		
-		git
-		master
-		https://github.com/Nano-Core/Nano.Examples.git
-		$(GitCommitHash)
-		true
-		true
-		true
-		snupkg
-		true
-		$(Version)
-		nano;example;sample;framework;library;learning;showcase
-		https://github.com/Nano-Core/Nano.Library/$(ProjectName)
-		LICENSE
-		icon.png
-		README.md
-		true
-	
-
-	
-		True
-	
-
-	
-		
-		
-		
-	
-
-	
-		
-	
-
-	
-	  
-	  
-	  
-	  
-	  
-	  
-	  
-	
-
-
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.sln b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.sln
deleted file mode 100644
index 39697721..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.sln
+++ /dev/null
@@ -1,134 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 18
-VisualStudioVersion = 18.1.11312.151
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}"
-	ProjectSection(SolutionItems) = preProject
-		.dockerignore = .dockerignore
-		.gitignore = .gitignore
-		Dockerfile = Dockerfile
-		icon.png = icon.png
-		LICENSE = LICENSE
-		README.md = README.md
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}"
-	ProjectSection(SolutionItems) = preProject
-		.kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml
-		.kubernetes\configmap.yaml = .kubernetes\configmap.yaml
-		.kubernetes\deployment.yaml = .kubernetes\deployment.yaml
-		.kubernetes\service.yaml = .kubernetes\service.yaml
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}"
-	ProjectSection(SolutionItems) = preProject
-		.github\config\slack.yml = .github\config\slack.yml
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}"
-	ProjectSection(SolutionItems) = preProject
-		.github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml
-	EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Robots.Models", "Api.PolicyHeaders.Robots.Models\Api.PolicyHeaders.Robots.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Robots", "Api.PolicyHeaders.Robots\Api.PolicyHeaders.Robots.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.Robots", ".tests\Tests.Api.PolicyHeaders.Robots\Tests.Api.PolicyHeaders.Robots.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}"
-EndProject
-Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU
-		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU
-		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU
-		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU
-		{345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-	GlobalSection(NestedProjects) = preSolution
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A}
-		{F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A}
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A}
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE}
-	EndGlobalSection
-EndGlobal
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.csproj b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.csproj
deleted file mode 100644
index 15b582a6..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.csproj
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-	
-		net10.0
-		
-		10.0.0.0
-		10.0.0.0
-		10.0.0.0
-		AnyCPU
-		Debug;Release
-		latest
-		en-US
-		enable
-		LICENSE
-		true
-		
-		false
-		true
-		false
-		true
-		Linux
-		..\docker-compose.dcproj
-	
-
-	
-		True
-	
-
-	
-		
-	
-
-	
-	    
-	
-
-
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Controllers/ExamplesController.cs b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Controllers/ExamplesController.cs
deleted file mode 100644
index 47df563c..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Controllers/ExamplesController.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using Nano.App.Api.Controllers;
-
-namespace Api.PolicyHeaders.Robots.Controllers;
-
-/// 
-/// Controller with examples.
-/// 
-/// The .
-public class ExamplesController(ILogger logger) : BaseController(logger)
-{
-    /// 
-    /// Robots Action.
-    /// 
-    /// The cancellation token.
-    /// A message.
-    /// Success.
-    [HttpGet]
-    [Route("robots")]
-    [ProducesResponseType((int)HttpStatusCode.OK)]
-    public virtual async Task RobotsAsync(CancellationToken cancellationToken = default)
-    {
-        await Task.CompletedTask;
-
-        return this.Ok("robots");
-    }
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local
deleted file mode 100644
index 8a943b01..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local
+++ /dev/null
@@ -1,6 +0,0 @@
-ARG DOTNET_ASPNET_VERSION="10.0"
-FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
-
-EXPOSE 8080
-
-ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Robots.dll"]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Program.cs b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Program.cs
deleted file mode 100644
index 32865375..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Program.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Nano.App.Api;
-
-NanoApiApplication
-    .ConfigureApp()
-    .ConfigureServices(_ =>
-    {
-        // Blank
-    })
-    .Build()
-    .Run();
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Properties/InternalsVisibleTo.cs
deleted file mode 100644
index 3b8eb8c2..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Properties/InternalsVisibleTo.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.Robots")]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Development.json b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Development.json
deleted file mode 100644
index 8593c62d..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Development.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Production.json b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Production.json
deleted file mode 100644
index 8593c62d..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Production.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Staging.json b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Staging.json
deleted file mode 100644
index 8593c62d..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Staging.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.json b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.json
deleted file mode 100644
index 50830b74..00000000
--- a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "App": {
-    "Version": "1.0.0.0",
-    "Hosting": {
-      "Root": "api",
-      "Http": {
-        "Ports": [
-          8080
-        ]
-      }
-    },
-    "HttpPolicyHeaders": {
-      "Robots": {
-        "UseNoIndex": true,
-        "UseNoFollow": true,
-        "UseNoSnippet": true,
-        "UseNoArchive": true,
-        "UseNoOdp": true,
-        "UseNoTranslate": true,
-        "UseNoImageIndex": true
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Dockerfile b/Api.PolicyHeaders.Robots/Dockerfile
deleted file mode 100644
index f349f074..00000000
--- a/Api.PolicyHeaders.Robots/Dockerfile
+++ /dev/null
@@ -1,35 +0,0 @@
-ARG DOTNET_SDK_VERSION
-ARG DOTNET_ASPNET_VERSION
-ARG CONTAINER_REGISTRY_SOURCE_LABEL
-ARG NUGET_HOST
-ARG NUGET_USERNAME
-ARG NUGET_PASSWORD
-
-FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
-
-LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL
-
-EXPOSE 8080
-WORKDIR /app
-
-FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build
-ARG NUGET_HOST
-ARG NUGET_USERNAME
-ARG NUGET_PASSWORD
-
-WORKDIR /src
-COPY . .
-
-RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text
-RUN dotnet build -c Release -o /app
-
-FROM build AS publish
-RUN dotnet publish -c Release -o /app
-
-FROM base AS final
-WORKDIR /app
-COPY --from=publish /app .
-ENV COMPlus_EnableDiagnostics=0
-ENV DOTNET_USE_POLLING_FILE_WATCHER=true
-
-ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Robots.dll"]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/LICENSE b/Api.PolicyHeaders.Robots/LICENSE
deleted file mode 100644
index 006e8c21..00000000
--- a/Api.PolicyHeaders.Robots/LICENSE
+++ /dev/null
@@ -1,18 +0,0 @@
-The MIT License (MIT)
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/README.md b/Api.PolicyHeaders.Robots/README.md
deleted file mode 100644
index 7aa12a4e..00000000
--- a/Api.PolicyHeaders.Robots/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Api.PolicyHeaders.Robots
-
-> _Nano API application with robots options._  
-_All lessons are complete, self-contained examples that include build and deployment setup._
-
-> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. 
-Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._
-
-> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio.
-
-> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**.
-
-***
-
-## Table of Contents
-* [Summary](#summary)
-* [Configuration](#configuration)
-
-## Summary
-This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller 
-that inherits from the top-level Nano `BaseController`.  
-
-This example shows the `X-Robots-Tag` header being set.  
-
-The following endpoint is available for testing.  
-
-| Endpoint                                     | Description                                                                |
-| -------------------------------------------- | -------------------------------------------------------------------------- |
-| `http://localhost:8080/api/examples/nosniff` | Returns a `200 OK` response including the `X-Robots-Tag` response header.  |
-
-> 📖 Learn more about **[Nano Content Type Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#robots)**.
-
-## Configuration
-```json
-"App": {
-  "HttpPolicyHeaders": {
-    "Robots": {
-      "UseNoIndex": true,
-      "UseNoFollow": true,
-      "UseNoSnippet": true,
-      "UseNoArchive": true,
-      "UseNoOdp": true,
-      "UseNoTranslate": true,
-      "UseNoImageIndex": true
-    }
-  }
-}
-```
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/icon.png b/Api.PolicyHeaders.Robots/icon.png
deleted file mode 100644
index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 14103
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
-
-  
-    2.1
-    Linux
-    false
-    http://localhost:{ServicePort}/docs
-    $(ProjectName)
-  
-  
-    
-  
-
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.docker/docker-compose.yml b/Api.PolicyHeaders.XssProtection/.docker/docker-compose.yml
deleted file mode 100644
index 01f78aba..00000000
--- a/Api.PolicyHeaders.XssProtection/.docker/docker-compose.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-services:
-  api.policyheaders.xssprotection:
-    image: api.policyheaders.xssprotection
-    hostname: api-policyheaders-xssprotection
-    restart: on-failure
-    ports:
-      - 8080:8080
-    build:
-      context: ../Api.PolicyHeaders.XssProtection
-      dockerfile: "Dockerfile.Local"
-    networks:
-      - network
-
-networks:
-  network:
-    name: network
-    driver: bridge
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.dockerignore b/Api.PolicyHeaders.XssProtection/.dockerignore
deleted file mode 100644
index e694ae21..00000000
--- a/Api.PolicyHeaders.XssProtection/.dockerignore
+++ /dev/null
@@ -1,12 +0,0 @@
-.dockerignore
-.env
-.git
-.gitignore
-.vs
-.vscode
-docker-compose.yml
-docker-compose.*.yml
-*/bin
-*/obj
-!obj/Docker/publish/*
-!obj/Docker/empty/
diff --git a/Api.PolicyHeaders.XssProtection/.github/config/slack.yml b/Api.PolicyHeaders.XssProtection/.github/config/slack.yml
deleted file mode 100644
index 3592affb..00000000
--- a/Api.PolicyHeaders.XssProtection/.github/config/slack.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-username: GitHub Actions
-icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png
-
-pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>."
-
-text: |
-  {{#if payload.commits}}
-  *Commits*
-  {{#each payload.commits}}
-  <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}}
-  {{/each}}
-  {{/if}}
-
-footer: >-
-  <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}}
-  
-fallback: |-
-  [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}}
diff --git a/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml
deleted file mode 100644
index 17384a75..00000000
--- a/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml
+++ /dev/null
@@ -1,171 +0,0 @@
-name: Build And Deploy
-on:
-  push
-env:
-  APP_NAME: Api.PolicyHeaders.XssProtection
-  IMAGE_NAME: api.policyheaders.xssprotection
-  SERVICE_NAME: api-policyheaders-xssprotection
-  VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}'
-  DOTNET_SDK_VERSION: 10.0
-  DOTNET_ASPNET_VERSION: 10.0
-  AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }}
-  AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
-  AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
-  AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
-  AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }}
-  NUGET_HOST: ${{ secrets.NUGET_HOST }}
-  NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }}
-  NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }}
-  NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }}
-  CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }}
-  CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }}
-  CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }}
-  CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }}
-  KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }}
-  KUBERNETES_NODEPOOL_COMPUTE: cpu
-  KUBERNETES_NAMESPACE: default
-  KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }}
-  KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }}
-  KUBERNETES_REPLICA_HISTORY_COUNT: 0
-  KUBERNETES_MEMORY_REQUEST: 512Mi   
-  KUBERNETES_MEMORY_LIMIT: 1536Mi
-  KUBERNETES_MEMORY_SCALING: 180
-  KUBERNETES_CPU_REQUEST: 200m
-  KUBERNETES_CPU_LIMIT: 600m
-  KUBERNETES_CPU_SCALING: 180
-  ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }}
-jobs:
-  build-and-deploy:
-    runs-on: ubuntu-latest
-    permissions:
-      contents: read
-      packages: write
-      id-token: write
-    concurrency:
-      group: ${{ github.repository }}
-      cancel-in-progress: true
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Azure Login
-        shell: pwsh
-        run: |
-          sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none;
-          sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none;
-          sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none;
-
-      - name: Build
-        shell: pwsh
-        run: |
-          dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text;
-
-          dotnet build -c Release .\$env:APP_NAME.sln;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-      - name: Test
-        shell: pwsh
-        run: |
-          dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj;
-          if ($LastExitCode -ne 0)
-          { 
-            throw "error";
-          };
-
-      - name: Publish Image
-        shell: pwsh
-        run: |
-          $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower()
-          $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest";
-          $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION
-          
-          sudo docker build `
-              -t $imageLatestTag `
-              -t $imageVersionTag `
-              --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION `
-              --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION `
-              --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL `
-              --build-arg NUGET_HOST=$env:NUGET_HOST `
-              --build-arg NUGET_USERNAME=$env:NUGET_USERNAME `
-              --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD `
-              ./;
-
-          if ($LastExitCode -ne 0) 
-          { 
-              throw "error";
-          };
-
-          sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST;
-          sudo docker push $imageLatestTag;
-          sudo docker push $imageVersionTag;
-          if ($LastExitCode -ne 0) 
-          { 
-              throw "error";
-          };  
-
-      - name: Publish NuGet
-        shell: pwsh
-        run: |
-          $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj";
-          dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build;
-          dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY;
-          if ($LastExitCode -ne 0) 
-          { 
-              throw "error";
-          };  
-
-      - name: Kubernetes Deploy
-        shell: pwsh
-        run: |
-         Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml;
-          sudo kubectl apply -f .kubernetes/service.tmp.yaml;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-          Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml;
-          sudo kubectl apply -f .kubernetes/configmap.tmp.yaml;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-          Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml;
-          sudo kubectl apply -f .kubernetes/deployment.tmp.yaml;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-          Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml;
-          sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml;
-          if ($LastExitCode -ne 0)
-          { 
-              throw "error";
-          };
-
-      - name: GitHub Release
-        if: github.ref == 'refs/heads/master'
-        uses: ncipollo/release-action@v1
-        with:
-          tag: v${{ env.VERSION }}
-          name: "Release ${{ env.VERSION }}"
-          body: |
-            Version: ${{ env.VERSION }}
-            Commit: ${{ github.sha }}
-          artifacts: "nupkgs/*"
-          token: ${{ secrets.GITHUB_TOKEN }}
-          draft: false
-          prerelease: false
-
-      - name: Slack Notification
-        if: always()
-        uses: act10ns/slack@v2
-        with:
-          webhook-url: ${{ secrets.SLACK_WEBHOOK }}
-          config: .github/config/slack.yml
-          status: ${{ job.status }}
-          channel: ${{ vars.SLACK_CHANNEL }}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.gitignore b/Api.PolicyHeaders.XssProtection/.gitignore
deleted file mode 100644
index 9921fc06..00000000
--- a/Api.PolicyHeaders.XssProtection/.gitignore
+++ /dev/null
@@ -1,11 +0,0 @@
-.vs
-*.user
-*.userprefs
-*.suo
-_ReSharper*
-**/bin
-**/obj
-*.DotSettings.User
-packages
-.env
-**/Properties/launchSettings.json
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/autoscaler.yaml
deleted file mode 100644
index 95add8ad..00000000
--- a/Api.PolicyHeaders.XssProtection/.kubernetes/autoscaler.yaml
+++ /dev/null
@@ -1,25 +0,0 @@
-apiVersion: autoscaling/v2
-kind: HorizontalPodAutoscaler
-metadata:
-  name: %SERVICE_NAME%-hpa
-  namespace: %KUBERNETES_NAMESPACE%
-spec:
-  minReplicas: %KUBERNETES_REPLICA_COUNT%
-  maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX%
-  scaleTargetRef:
-    apiVersion: apps/v1
-    kind: Deployment
-    name: %SERVICE_NAME%
-  metrics:
-    - type: Resource
-      resource:
-        name: cpu
-        target:
-          type: Utilization
-          averageUtilization: %KUBERNETES_CPU_SCALING%
-    - type: Resource
-      resource:
-        name: memory
-        target:
-          type: Utilization
-          averageUtilization: %KUBERNETES_MEMORY_SCALING%
diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml
deleted file mode 100644
index 3977c0ee..00000000
--- a/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml
+++ /dev/null
@@ -1,8 +0,0 @@
-  apiVersion: v1
-kind: ConfigMap
-metadata:
-  name: %SERVICE_NAME%-config
-  namespace: %KUBERNETES_NAMESPACE%
-data:
-  App__Version: %VERSION%
-  ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT%
diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml
deleted file mode 100644
index fdc5dfdf..00000000
--- a/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml
+++ /dev/null
@@ -1,83 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  name: %SERVICE_NAME%
-  namespace: %KUBERNETES_NAMESPACE%
-  labels:
-    app: %SERVICE_NAME%
-spec:
-  replicas: %KUBERNETES_REPLICA_COUNT%
-  revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT%
-  selector:
-    matchLabels:
-      app: %SERVICE_NAME%
-  template:
-    metadata:
-      labels:
-        app: %SERVICE_NAME%
-    spec:
-      automountServiceAccountToken: false
-      securityContext:
-        runAsUser: 1000
-        runAsGroup: 2000 
-      topologySpreadConstraints:
-        - maxSkew: 1
-          topologyKey: kubernetes.io/hostname
-          whenUnsatisfiable: ScheduleAnyway
-          labelSelector:
-            matchLabels:
-              app: %SERVICE_NAME%  
-      automountServiceAccountToken: false
-      nodeSelector:
-        nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE%
-        kubernetes.io/os: linux
-      containers:
-      - name: %SERVICE_NAME%
-        image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION%
-        ports:
-        - containerPort: 8080
-        imagePullPolicy: Always 
-        envFrom:
-        - configMapRef:
-            name: %SERVICE_NAME%-config
-        resources:
-          requests:
-            memory: %KUBERNETES_MEMORY_REQUEST%
-            cpu: %KUBERNETES_CPU_REQUEST%
-          limits:
-            memory: %KUBERNETES_MEMORY_LIMIT%
-            cpu: %KUBERNETES_CPU_LIMIT%
-        securityContext:
-          privileged: false
-          allowPrivilegeEscalation: false
-          readOnlyRootFilesystem: true
-          runAsNonRoot: true
-          runAsUser: 1000
-          runAsGroup: 2000
-          capabilities:
-            drop:
-            - ALL
-        livenessProbe:
-          httpGet:
-            path: /healthz
-            port: 8080
-            scheme: HTTP
-          periodSeconds: 10
-          initialDelaySeconds: 30
-          timeoutSeconds: 2
-        readinessProbe:
-          httpGet:
-            path: /healthz
-            port: 8080
-            scheme: HTTP
-          periodSeconds: 5
-          initialDelaySeconds: 20
-          timeoutSeconds: 2
-        volumeMounts:
-        - name: tmp
-          mountPath: /tmp
-        - name: %SERVICE_NAME%-volume
-          mountPath: /mnt/%STORAGE_SHARE_NAME%
-      imagePullSecrets:
-        - name: ghcr-pull-secret
-
diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/service.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/service.yaml
deleted file mode 100644
index 2d8e20b2..00000000
--- a/Api.PolicyHeaders.XssProtection/.kubernetes/service.yaml
+++ /dev/null
@@ -1,12 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
-  name: %SERVICE_NAME%
-  namespace: %KUBERNETES_NAMESPACE%
-spec:
-  ports:  
-  - name: http
-    port: 8080
-  selector:
-    app: %SERVICE_NAME%
-  type: ClusterIP
diff --git a/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Properties/DoNotParallelize.cs
deleted file mode 100644
index 6a076669..00000000
--- a/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Properties/DoNotParallelize.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-[assembly: DoNotParallelize]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Tests.Api.PolicyHeaders.XssProtection.csproj b/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Tests.Api.PolicyHeaders.XssProtection.csproj
deleted file mode 100644
index a1a6ef58..00000000
--- a/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Tests.Api.PolicyHeaders.XssProtection.csproj
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-	
-		net10.0
-		false
-		latest
-		
-		
-		true
-	
-
-	
-		
-		
-		
-	
-
-	
-	  
-	  
-	
-
-
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj
deleted file mode 100644
index 890be2f1..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-	
-		net10.0
-		
-		10.0.0.0
-		10.0.0.0
-		10.0.0.0
-		AnyCPU
-		Debug;Release
-		latest
-		en-US
-		enable
-		LICENSE
-		true
-		
-		True
-		true
-		true
-		true
-		Michael Vivet
-		Michael Vivet
-		$(ProjectName)
-		Example project demonstrating a focused aspect of the Nano Library.
-		
-			A practical example highlighting a specific area or scenario of Nano for learning
-			and experimentation. Shows how features or patterns can be applied without
-			representing a full application or complete reference.
-		
-		
-			- Added .NET 10 support
-		
-		git
-		master
-		https://github.com/Nano-Core/Nano.Examples.git
-		$(GitCommitHash)
-		true
-		true
-		true
-		snupkg
-		true
-		$(Version)
-		nano;example;sample;framework;library;learning;showcase
-		https://github.com/Nano-Core/Nano.Library/$(ProjectName)
-		LICENSE
-		icon.png
-		README.md
-		true
-	
-
-	
-		True
-	
-
-	
-		
-		
-		
-	
-
-	
-		
-	
-
-	
-	  
-	  
-	  
-	  
-	  
-	  
-	  
-	
-
-
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.sln b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.sln
deleted file mode 100644
index 29aeb199..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.sln
+++ /dev/null
@@ -1,134 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 18
-VisualStudioVersion = 18.1.11312.151
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}"
-	ProjectSection(SolutionItems) = preProject
-		.dockerignore = .dockerignore
-		.gitignore = .gitignore
-		Dockerfile = Dockerfile
-		icon.png = icon.png
-		LICENSE = LICENSE
-		README.md = README.md
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}"
-	ProjectSection(SolutionItems) = preProject
-		.kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml
-		.kubernetes\configmap.yaml = .kubernetes\configmap.yaml
-		.kubernetes\deployment.yaml = .kubernetes\deployment.yaml
-		.kubernetes\service.yaml = .kubernetes\service.yaml
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}"
-	ProjectSection(SolutionItems) = preProject
-		.github\config\slack.yml = .github\config\slack.yml
-	EndProjectSection
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}"
-	ProjectSection(SolutionItems) = preProject
-		.github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml
-	EndProjectSection
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.XssProtection.Models", "Api.PolicyHeaders.XssProtection.Models\Api.PolicyHeaders.XssProtection.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.XssProtection", "Api.PolicyHeaders.XssProtection\Api.PolicyHeaders.XssProtection.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.XssProtection", ".tests\Tests.Api.PolicyHeaders.XssProtection\Tests.Api.PolicyHeaders.XssProtection.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}"
-EndProject
-Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU
-		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU
-		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU
-		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU
-		{345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-	GlobalSection(NestedProjects) = preSolution
-		{50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A}
-		{F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A}
-		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}
-		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1}
-		{345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A}
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE}
-	EndGlobalSection
-EndGlobal
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.csproj b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.csproj
deleted file mode 100644
index 792b3a4e..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.csproj
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-	
-		net10.0
-		
-		10.0.0.0
-		10.0.0.0
-		10.0.0.0
-		AnyCPU
-		Debug;Release
-		latest
-		en-US
-		enable
-		LICENSE
-		true
-		
-		false
-		true
-		false
-		true
-		Linux
-		..\docker-compose.dcproj
-	
-
-	
-		True
-	
-
-	
-		
-	
-
-	
-	    
-	
-
-
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Controllers/ExamplesController.cs b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Controllers/ExamplesController.cs
deleted file mode 100644
index 671d077e..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Controllers/ExamplesController.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.Net;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Logging;
-using Nano.App.Api.Controllers;
-
-namespace Api.PolicyHeaders.XssProtection.Controllers;
-
-/// 
-/// Controller with examples.
-/// 
-/// The .
-public class ExamplesController(ILogger logger) : BaseController(logger)
-{
-    /// 
-    /// Xss Action.
-    /// 
-    /// The cancellation token.
-    /// A message.
-    /// Success.
-    [HttpGet]
-    [Route("xss")]
-    [ProducesResponseType((int)HttpStatusCode.OK)]
-    public virtual async Task XssAsync(CancellationToken cancellationToken = default)
-    {
-        await Task.CompletedTask;
-
-        return this.Ok("xss");
-    }
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local
deleted file mode 100644
index 0564fe65..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local
+++ /dev/null
@@ -1,6 +0,0 @@
-ARG DOTNET_ASPNET_VERSION="10.0"
-FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
-
-EXPOSE 8080
-
-ENTRYPOINT ["dotnet", "Api.PolicyHeaders.XssProtection.dll"]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Program.cs b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Program.cs
deleted file mode 100644
index 32865375..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Program.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using Nano.App.Api;
-
-NanoApiApplication
-    .ConfigureApp()
-    .ConfigureServices(_ =>
-    {
-        // Blank
-    })
-    .Build()
-    .Run();
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Properties/InternalsVisibleTo.cs
deleted file mode 100644
index bc3b7710..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Properties/InternalsVisibleTo.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-using System.Runtime.CompilerServices;
-
-[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.XssProtection")]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Development.json b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Development.json
deleted file mode 100644
index 8593c62d..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Development.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Production.json b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Production.json
deleted file mode 100644
index 8593c62d..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Production.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Staging.json b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Staging.json
deleted file mode 100644
index 8593c62d..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Staging.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.json b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.json
deleted file mode 100644
index 9c580799..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "App": {
-    "Version": "1.0.0.0",
-    "Hosting": {
-      "Root": "api",
-      "Http": {
-        "Ports": [
-          8080
-        ]
-      }
-    },
-    "HttpPolicyHeaders": {
-      "XssProtection": {
-        "XssProtectionPolicyHeader": "FilterEnabledBlockMode",
-        "ReportingUrl": null
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/wwwroot/xss-violation.html b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/wwwroot/xss-violation.html
deleted file mode 100644
index 288c3666..00000000
--- a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/wwwroot/xss-violation.html
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-    
-    XSS Protection Test
-
-
-    

XSS Protection Test

- -

If X-XSS-Protection is working with block mode, this page should NOT render and the no alert should show.

- - - - \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/Dockerfile b/Api.PolicyHeaders.XssProtection/Dockerfile deleted file mode 100644 index b9e1371d..00000000 --- a/Api.PolicyHeaders.XssProtection/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.PolicyHeaders.XssProtection.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/LICENSE b/Api.PolicyHeaders.XssProtection/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.PolicyHeaders.XssProtection/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/README.md b/Api.PolicyHeaders.XssProtection/README.md deleted file mode 100644 index 0b4af4e4..00000000 --- a/Api.PolicyHeaders.XssProtection/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Api.PolicyHeaders.XssProtection - -> _Nano API application with xxs protection._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -Run the endpoint to inspect the `X-XSS-Protection` header in the response. Then load the provided HTML page to observe that the `alert` is blocked by the browser. - -Note that `X-XSS-Protection` is deprecated and ignored by modern browsers, including: -* Chrome -* Edge (Chromium) -* Firefox - -> ⚠️ This header is considered a legacy defense-in-depth mechanism and has been largely replaced by `Content-Security-Policy`. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ---------------------------------------- | ------------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/xss` | Returns a `200 OK` response including the `X-XSS-Protection` response header. | - -> 📖 Learn more about **[Nano Xxs Protection Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#xxs-protection)**. - -## Configuration -```json -"App": { - "HttpPolicyHeaders": { - "XssProtection": { - "XssProtectionPolicyHeader": "FilterEnabledBlockMode" - } - } -} -``` \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/icon.png b/Api.PolicyHeaders.XssProtection/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~
- - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.RequestTracing/.docker/docker-compose.yml b/Api.RequestTracing/.docker/docker-compose.yml deleted file mode 100644 index a0cfd6ed..00000000 --- a/Api.RequestTracing/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.requesttracing: - image: api.requesttracing - hostname: api-requesttracing - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.RequestTracing - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.RequestTracing/.dockerignore b/Api.RequestTracing/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.RequestTracing/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.RequestTracing/.github/config/slack.yml b/Api.RequestTracing/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.RequestTracing/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.RequestTracing/.github/workflows/build-and-deploy.yml b/Api.RequestTracing/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 545c2221..00000000 --- a/Api.RequestTracing/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.RequestTracing - IMAGE_NAME: api.requesttracing - SERVICE_NAME: api-requesttracing - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.RequestTracing/.gitignore b/Api.RequestTracing/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.RequestTracing/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.RequestTracing/.kubernetes/autoscaler.yaml b/Api.RequestTracing/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.RequestTracing/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.RequestTracing/.kubernetes/configmap.yaml b/Api.RequestTracing/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.RequestTracing/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.RequestTracing/.kubernetes/deployment.yaml b/Api.RequestTracing/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.RequestTracing/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.RequestTracing/.kubernetes/service.yaml b/Api.RequestTracing/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.RequestTracing/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Properties/DoNotParallelize.cs b/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Tests.Api.RequestTracing.csproj b/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Tests.Api.RequestTracing.csproj deleted file mode 100644 index 45afd36c..00000000 --- a/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Tests.Api.RequestTracing.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj b/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing.sln b/Api.RequestTracing/Api.RequestTracing.sln deleted file mode 100644 index ad4eefca..00000000 --- a/Api.RequestTracing/Api.RequestTracing.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.RequestTracing.Models", "Api.RequestTracing.Models\Api.RequestTracing.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.RequestTracing", "Api.RequestTracing\Api.RequestTracing.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.RequestTracing", ".tests\Tests.Api.RequestTracing\Tests.Api.RequestTracing.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.RequestTracing/Api.RequestTracing/Api.RequestTracing.csproj b/Api.RequestTracing/Api.RequestTracing/Api.RequestTracing.csproj deleted file mode 100644 index 6810272d..00000000 --- a/Api.RequestTracing/Api.RequestTracing/Api.RequestTracing.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/Controllers/ExamplesController.cs b/Api.RequestTracing/Api.RequestTracing/Controllers/ExamplesController.cs deleted file mode 100644 index dd3f2e90..00000000 --- a/Api.RequestTracing/Api.RequestTracing/Controllers/ExamplesController.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.RequestTracing.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Request Tracing Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("request-tracing")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task RequestTracingAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok(new - { - this.RequestId - }); - } -} \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local b/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local deleted file mode 100644 index 87cc5040..00000000 --- a/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.RequestTracing.dll"] \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/Program.cs b/Api.RequestTracing/Api.RequestTracing/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.RequestTracing/Api.RequestTracing/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.RequestTracing/Api.RequestTracing/Properties/InternalsVisibleTo.cs b/Api.RequestTracing/Api.RequestTracing/Properties/InternalsVisibleTo.cs deleted file mode 100644 index fae87874..00000000 --- a/Api.RequestTracing/Api.RequestTracing/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.RequestTracing")] \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/appsettings.Development.json b/Api.RequestTracing/Api.RequestTracing/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.RequestTracing/Api.RequestTracing/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/appsettings.Production.json b/Api.RequestTracing/Api.RequestTracing/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.RequestTracing/Api.RequestTracing/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/appsettings.Staging.json b/Api.RequestTracing/Api.RequestTracing/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.RequestTracing/Api.RequestTracing/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/appsettings.json b/Api.RequestTracing/Api.RequestTracing/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.RequestTracing/Api.RequestTracing/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.RequestTracing/Dockerfile b/Api.RequestTracing/Dockerfile deleted file mode 100644 index 316aafa3..00000000 --- a/Api.RequestTracing/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.RequestTracing.dll"] \ No newline at end of file diff --git a/Api.RequestTracing/LICENSE b/Api.RequestTracing/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.RequestTracing/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.RequestTracing/README.md b/Api.RequestTracing/README.md deleted file mode 100644 index 6d4393b2..00000000 --- a/Api.RequestTracing/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Api.RequestTracing - -> _Nano API application with request tracing._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -Try passing the `X-Request-Id` request header and observe that Nano uses it and returns the same value in the response. -If no `X-Request-Id` header is provided, Nano automatically generates one and returns it in the response. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ----------------------------------------------------- | -------------------------------------- | -| `http://localhost:8080/api/examples/request-tracing` | Returns a simple `200 OK` response. | - -> 📖 Learn more about **[Nano Request Tracing](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#request-tracing)**. diff --git a/Api.RequestTracing/icon.png b/Api.RequestTracing/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.ResponseCache/.docker/docker-compose.yml b/Api.ResponseCache/.docker/docker-compose.yml deleted file mode 100644 index 641f6e5b..00000000 --- a/Api.ResponseCache/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.responsecache: - image: api.responsecache - hostname: api-responsecache - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.ResponseCache - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.ResponseCache/.dockerignore b/Api.ResponseCache/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.ResponseCache/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.ResponseCache/.github/config/slack.yml b/Api.ResponseCache/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.ResponseCache/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ResponseCache/.github/workflows/build-and-deploy.yml b/Api.ResponseCache/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 28fe868d..00000000 --- a/Api.ResponseCache/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.ResponseCache - IMAGE_NAME: api.responsecache - SERVICE_NAME: api-responsecache - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ResponseCache/.gitignore b/Api.ResponseCache/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.ResponseCache/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ResponseCache/.kubernetes/autoscaler.yaml b/Api.ResponseCache/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.ResponseCache/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ResponseCache/.kubernetes/configmap.yaml b/Api.ResponseCache/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.ResponseCache/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ResponseCache/.kubernetes/deployment.yaml b/Api.ResponseCache/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.ResponseCache/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ResponseCache/.kubernetes/service.yaml b/Api.ResponseCache/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.ResponseCache/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Properties/DoNotParallelize.cs b/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Tests.Api.ResponseCache.csproj b/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Tests.Api.ResponseCache.csproj deleted file mode 100644 index 9df93a74..00000000 --- a/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Tests.Api.ResponseCache.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj b/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache.sln b/Api.ResponseCache/Api.ResponseCache.sln deleted file mode 100644 index c2ea2ac0..00000000 --- a/Api.ResponseCache/Api.ResponseCache.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ResponseCache.Models", "Api.ResponseCache.Models\Api.ResponseCache.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ResponseCache", "Api.ResponseCache\Api.ResponseCache.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ResponseCache", ".tests\Tests.Api.ResponseCache\Tests.Api.ResponseCache.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.ResponseCache/Api.ResponseCache/Api.ResponseCache.csproj b/Api.ResponseCache/Api.ResponseCache/Api.ResponseCache.csproj deleted file mode 100644 index 92eed437..00000000 --- a/Api.ResponseCache/Api.ResponseCache/Api.ResponseCache.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/Controllers/ExamplesController.cs b/Api.ResponseCache/Api.ResponseCache/Controllers/ExamplesController.cs deleted file mode 100644 index 835cd98f..00000000 --- a/Api.ResponseCache/Api.ResponseCache/Controllers/ExamplesController.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.ResponseCache.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Response Cache Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("response-cache")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task ResponseCacheAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("response-cache"); - } - - /// - /// No Response Cache Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("no-response-cache")] - [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task NoResponseCacheAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("no-response-cache"); - } -} \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local b/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local deleted file mode 100644 index 7062f25b..00000000 --- a/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ResponseCache.dll"] \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/Program.cs b/Api.ResponseCache/Api.ResponseCache/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ResponseCache/Api.ResponseCache/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ResponseCache/Api.ResponseCache/Properties/InternalsVisibleTo.cs b/Api.ResponseCache/Api.ResponseCache/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 9db9ee24..00000000 --- a/Api.ResponseCache/Api.ResponseCache/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ResponseCache")] \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/appsettings.Development.json b/Api.ResponseCache/Api.ResponseCache/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ResponseCache/Api.ResponseCache/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/appsettings.Production.json b/Api.ResponseCache/Api.ResponseCache/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ResponseCache/Api.ResponseCache/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/appsettings.Staging.json b/Api.ResponseCache/Api.ResponseCache/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ResponseCache/Api.ResponseCache/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/appsettings.json b/Api.ResponseCache/Api.ResponseCache/appsettings.json deleted file mode 100644 index 239b0b01..00000000 --- a/Api.ResponseCache/Api.ResponseCache/appsettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "ResponseCache": { - "MaxSize": 1024, - "MaxBodySize": 102400, - "MaxAge": "00:20:00" - } - } -} \ No newline at end of file diff --git a/Api.ResponseCache/Dockerfile b/Api.ResponseCache/Dockerfile deleted file mode 100644 index 3dbc645f..00000000 --- a/Api.ResponseCache/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.ResponseCache.dll"] \ No newline at end of file diff --git a/Api.ResponseCache/LICENSE b/Api.ResponseCache/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.ResponseCache/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.ResponseCache/README.md b/Api.ResponseCache/README.md deleted file mode 100644 index 891d1367..00000000 --- a/Api.ResponseCache/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Api.ResponseCache - -> _Nano API application with response cache._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example demonstrates using response caching with a Nano application. - -The following endpoint is available for testing. - -| Endpoint | Description | -| -------------------------------------------------------| ------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/response-cache` | Returns a `200 OK` response cached. Header: `Cache-Control=public, max-age=1200` | -| `http://localhost:8080/api/examples/no-response-cache` | Returns a `200 OK` response with no cache using Header: `[ResponseCache]`. `Cache-Control=no-store,no-cache` | - -> 📖 Learn more about **[Nano Response Cache](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#response-cache)**. - -## Configuration -```json -"App": { - "ResponseCache": { - "MaxSize": 1024, - "MaxBodySize": 102400, - "MaxAge": "00:20:00" - } -} -``` diff --git a/Api.ResponseCache/icon.png b/Api.ResponseCache/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.ResponseCompression/.docker/docker-compose.yml b/Api.ResponseCompression/.docker/docker-compose.yml deleted file mode 100644 index e88bc020..00000000 --- a/Api.ResponseCompression/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.responsecompression: - image: api.responsecompression - hostname: api-responsecompression - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.ResponseCompression - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.ResponseCompression/.dockerignore b/Api.ResponseCompression/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.ResponseCompression/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.ResponseCompression/.github/config/slack.yml b/Api.ResponseCompression/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.ResponseCompression/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ResponseCompression/.github/workflows/build-and-deploy.yml b/Api.ResponseCompression/.github/workflows/build-and-deploy.yml deleted file mode 100644 index f3d94087..00000000 --- a/Api.ResponseCompression/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.ResponseCompression - IMAGE_NAME: api.responsecompression - SERVICE_NAME: api-responsecompression - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ResponseCompression/.gitignore b/Api.ResponseCompression/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.ResponseCompression/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ResponseCompression/.kubernetes/autoscaler.yaml b/Api.ResponseCompression/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.ResponseCompression/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ResponseCompression/.kubernetes/configmap.yaml b/Api.ResponseCompression/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.ResponseCompression/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ResponseCompression/.kubernetes/deployment.yaml b/Api.ResponseCompression/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.ResponseCompression/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.ResponseCompression/.kubernetes/service.yaml b/Api.ResponseCompression/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.ResponseCompression/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Properties/DoNotParallelize.cs b/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Tests.Api.ResponseCompression.csproj b/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Tests.Api.ResponseCompression.csproj deleted file mode 100644 index e0eb0a25..00000000 --- a/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Tests.Api.ResponseCompression.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj b/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression.sln b/Api.ResponseCompression/Api.ResponseCompression.sln deleted file mode 100644 index f93b7bfb..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ResponseCompression.Models", "Api.ResponseCompression.Models\Api.ResponseCompression.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ResponseCompression", "Api.ResponseCompression\Api.ResponseCompression.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ResponseCompression", ".tests\Tests.Api.ResponseCompression\Tests.Api.ResponseCompression.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.ResponseCompression/Api.ResponseCompression/Api.ResponseCompression.csproj b/Api.ResponseCompression/Api.ResponseCompression/Api.ResponseCompression.csproj deleted file mode 100644 index ed8e4ad2..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/Api.ResponseCompression.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/Controllers/ExamplesController.cs b/Api.ResponseCompression/Api.ResponseCompression/Controllers/ExamplesController.cs deleted file mode 100644 index 06c12f78..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/Controllers/ExamplesController.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Common.Consts; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace Api.ResponseCompression.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Response Compression Action. - /// - /// The cancellation token. - /// 1024 KB content. - /// Success. - [HttpGet] - [Route("response-compression")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task CompressedAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - var block = new string('A', 1024); - var payload = string.Concat(Enumerable.Repeat(block, 1024)); - - return this.Content(payload, HttpContentType.TEXT); - } -} \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local b/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local deleted file mode 100644 index 53c17eb8..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.ResponseCompression.dll"] \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/Program.cs b/Api.ResponseCompression/Api.ResponseCompression/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.ResponseCompression/Api.ResponseCompression/Properties/InternalsVisibleTo.cs b/Api.ResponseCompression/Api.ResponseCompression/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 19af86ea..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.ResponseCompression")] \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Development.json b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Production.json b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Staging.json b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/appsettings.json b/Api.ResponseCompression/Api.ResponseCompression/appsettings.json deleted file mode 100644 index 79f4ab41..00000000 --- a/Api.ResponseCompression/Api.ResponseCompression/appsettings.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "ResponseCompression": { - "UseGzip": true, - "UseBrotli": true - } - } -} \ No newline at end of file diff --git a/Api.ResponseCompression/Dockerfile b/Api.ResponseCompression/Dockerfile deleted file mode 100644 index e0f4803d..00000000 --- a/Api.ResponseCompression/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.ResponseCompression.dll"] \ No newline at end of file diff --git a/Api.ResponseCompression/LICENSE b/Api.ResponseCompression/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.ResponseCompression/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.ResponseCompression/README.md b/Api.ResponseCompression/README.md deleted file mode 100644 index befe3eb6..00000000 --- a/Api.ResponseCompression/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Api.ResponseCompression - -> _Nano API application with response compression._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -When the endpoint is invoked with the Accept-Encoding header set to gzip, deflate, br, the response will be compressed. -If this header is not present, the response will be sent uncompressed. - -The following endpoint is available for testing. - -| Endpoint | Description | -| --------------------------------------------------------- | ------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/response-compression` | Returns a `200 OK` response. Headers: `Content-Encoding: gzip` and `Vary: Accept-Encoding` | - -> 📖 Learn more about **[Nano Response Compression](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#response-compression)**. - -## Configuration -```json -"App": { - "ResponseCompression": { - "UseGzip": true, - "UseBrotli": true - } -} -``` diff --git a/Api.ResponseCompression/icon.png b/Api.ResponseCompression/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Session/.docker/docker-compose.yml b/Api.Session/.docker/docker-compose.yml deleted file mode 100644 index 3c71b4fb..00000000 --- a/Api.Session/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.session: - image: api.session - hostname: api-session - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Session - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Session/.dockerignore b/Api.Session/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Session/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Session/.github/config/slack.yml b/Api.Session/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Session/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Session/.github/workflows/build-and-deploy.yml b/Api.Session/.github/workflows/build-and-deploy.yml deleted file mode 100644 index e923dbb2..00000000 --- a/Api.Session/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Session - IMAGE_NAME: api.session - SERVICE_NAME: api-session - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Session/.gitignore b/Api.Session/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Session/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Session/.kubernetes/autoscaler.yaml b/Api.Session/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Session/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Session/.kubernetes/configmap.yaml b/Api.Session/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Session/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Session/.kubernetes/deployment.yaml b/Api.Session/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Session/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Session/.kubernetes/service.yaml b/Api.Session/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Session/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Session/.tests/Tests.Api.Session/Properties/DoNotParallelize.cs b/Api.Session/.tests/Tests.Api.Session/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Session/.tests/Tests.Api.Session/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Session/.tests/Tests.Api.Session/Tests.Api.Session.csproj b/Api.Session/.tests/Tests.Api.Session/Tests.Api.Session.csproj deleted file mode 100644 index 62452e65..00000000 --- a/Api.Session/.tests/Tests.Api.Session/Tests.Api.Session.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Session/Api.Session.Models/Api.Session.Models.csproj b/Api.Session/Api.Session.Models/Api.Session.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Session/Api.Session.Models/Api.Session.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Session/Api.Session.sln b/Api.Session/Api.Session.sln deleted file mode 100644 index ce11032c..00000000 --- a/Api.Session/Api.Session.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Session.Models", "Api.Session.Models\Api.Session.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Session", "Api.Session\Api.Session.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Session", ".tests\Tests.Api.Session\Tests.Api.Session.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Session/Api.Session/Api.Session.csproj b/Api.Session/Api.Session/Api.Session.csproj deleted file mode 100644 index 1b059621..00000000 --- a/Api.Session/Api.Session/Api.Session.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Session/Api.Session/Controllers/ExamplesController.cs b/Api.Session/Api.Session/Controllers/ExamplesController.cs deleted file mode 100644 index f06cc811..00000000 --- a/Api.Session/Api.Session/Controllers/ExamplesController.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace Api.Session.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - private const string SESSION_KEY = "TestKey"; - - /// - /// Set Session Action. - /// - /// The value to set in session. - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("set-session")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task SetSessionAsync([FromQuery][Required]string value, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - this.HttpContext.Session - .SetString(SESSION_KEY, value); - - return this.Ok("session set"); - } - - /// - /// Get Session Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("get-session")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task GetSession(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - var value = this.HttpContext.Session - .GetString(SESSION_KEY); - - if (value == null) - { - return Ok("session is empty or expired"); - } - - return Ok($"session value: {value}"); - } - - /// - /// Clear Session Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("clear-session")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task ClearSession(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - this.HttpContext.Session - .Clear(); - - return Ok("sesssion cleared"); - } -} \ No newline at end of file diff --git a/Api.Session/Api.Session/Dockerfile.Local b/Api.Session/Api.Session/Dockerfile.Local deleted file mode 100644 index fb4b06aa..00000000 --- a/Api.Session/Api.Session/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Session.dll"] \ No newline at end of file diff --git a/Api.Session/Api.Session/Program.cs b/Api.Session/Api.Session/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Session/Api.Session/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Session/Api.Session/Properties/InternalsVisibleTo.cs b/Api.Session/Api.Session/Properties/InternalsVisibleTo.cs deleted file mode 100644 index d1859b8f..00000000 --- a/Api.Session/Api.Session/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Session")] \ No newline at end of file diff --git a/Api.Session/Api.Session/appsettings.Development.json b/Api.Session/Api.Session/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Session/Api.Session/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Session/Api.Session/appsettings.Production.json b/Api.Session/Api.Session/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Session/Api.Session/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Session/Api.Session/appsettings.Staging.json b/Api.Session/Api.Session/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Session/Api.Session/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Session/Api.Session/appsettings.json b/Api.Session/Api.Session/appsettings.json deleted file mode 100644 index 901094d0..00000000 --- a/Api.Session/Api.Session/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "Session": { - "Timeout": "00:20:00" - } - } -} \ No newline at end of file diff --git a/Api.Session/Dockerfile b/Api.Session/Dockerfile deleted file mode 100644 index 7b0f7c94..00000000 --- a/Api.Session/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Session.dll"] \ No newline at end of file diff --git a/Api.Session/LICENSE b/Api.Session/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Session/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Session/README.md b/Api.Session/README.md deleted file mode 100644 index 1fcd95b4..00000000 --- a/Api.Session/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Api.Session - -> _Nano API application with session enabled._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example demonstrates using session with a Nano application. - -The following endpoints is available for testing. - -| Endpoint | Description | -| -------------------------------------------------- | --------------------------------------------------------- | -| `http://localhost:8080/api/examples/set-session` | Sets a session variable and returns a `200 OK`. | -| `http://localhost:8080/api/examples/get-session` | Gets the session variable if set and returns a `200 OK`. | -| `http://localhost:8080/api/examples/clear-session` | Clear the session and returns a `200 OK`. | - -> 📖 Learn more about **[Nano Session](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#session)**. - -## Configuration -```json -"App": { - "Session": { - "Timeout": "00:20:00" - } -} -``` \ No newline at end of file diff --git a/Api.Session/icon.png b/Api.Session/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.StartupTasks/.docker/docker-compose.yml b/Api.StartupTasks/.docker/docker-compose.yml deleted file mode 100644 index 37494314..00000000 --- a/Api.StartupTasks/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.startuptasks: - image: api.startuptasks - hostname: api-startuptasks - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.StartupTasks - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.StartupTasks/.dockerignore b/Api.StartupTasks/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.StartupTasks/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.StartupTasks/.github/config/slack.yml b/Api.StartupTasks/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.StartupTasks/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.StartupTasks/.github/workflows/build-and-deploy.yml b/Api.StartupTasks/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 2c7519ac..00000000 --- a/Api.StartupTasks/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.StartupTasks - IMAGE_NAME: api.startuptasks - SERVICE_NAME: api-startuptasks - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.StartupTasks/.gitignore b/Api.StartupTasks/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.StartupTasks/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.StartupTasks/.kubernetes/autoscaler.yaml b/Api.StartupTasks/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.StartupTasks/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.StartupTasks/.kubernetes/configmap.yaml b/Api.StartupTasks/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.StartupTasks/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.StartupTasks/.kubernetes/deployment.yaml b/Api.StartupTasks/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.StartupTasks/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.StartupTasks/.kubernetes/service.yaml b/Api.StartupTasks/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.StartupTasks/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Properties/DoNotParallelize.cs b/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Tests.Api.StartupTasks.csproj b/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Tests.Api.StartupTasks.csproj deleted file mode 100644 index bef76c37..00000000 --- a/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Tests.Api.StartupTasks.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj b/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks.sln b/Api.StartupTasks/Api.StartupTasks.sln deleted file mode 100644 index a32b7109..00000000 --- a/Api.StartupTasks/Api.StartupTasks.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.StartupTasks.Models", "Api.StartupTasks.Models\Api.StartupTasks.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.StartupTasks", "Api.StartupTasks\Api.StartupTasks.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.StartupTasks", ".tests\Tests.Api.StartupTasks\Tests.Api.StartupTasks.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.StartupTasks/Api.StartupTasks/Api.StartupTasks.csproj b/Api.StartupTasks/Api.StartupTasks/Api.StartupTasks.csproj deleted file mode 100644 index d6660e37..00000000 --- a/Api.StartupTasks/Api.StartupTasks/Api.StartupTasks.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/Controllers/ExamplesController.cs b/Api.StartupTasks/Api.StartupTasks/Controllers/ExamplesController.cs deleted file mode 100644 index beb98c91..00000000 --- a/Api.StartupTasks/Api.StartupTasks/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.StartupTasks.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Startup Tasks Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("startup-tasks")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task StartupTaskAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("startup-tasks"); - } -} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local b/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local deleted file mode 100644 index 1a5748f0..00000000 --- a/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.StartupTasks.dll"] \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/Program.cs b/Api.StartupTasks/Api.StartupTasks/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.StartupTasks/Api.StartupTasks/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.StartupTasks/Api.StartupTasks/Properties/InternalsVisibleTo.cs b/Api.StartupTasks/Api.StartupTasks/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 0d6b6c6a..00000000 --- a/Api.StartupTasks/Api.StartupTasks/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.StartupTasks")] \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/Startup/ExampleStartupTask.cs b/Api.StartupTasks/Api.StartupTasks/Startup/ExampleStartupTask.cs deleted file mode 100644 index fd82827a..00000000 --- a/Api.StartupTasks/Api.StartupTasks/Startup/ExampleStartupTask.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Startup; - -namespace Api.StartupTasks.Startup; - -/// -/// Example Startup Task. -/// -/// The . -public class ExampleStartupTask(ILogger logger) : BaseStartupTask(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - Console.WriteLine("Example Startup Task Started..."); - - await Task.Delay(20000, cancellationToken); - - Console.WriteLine(DateTime.UtcNow); - - Console.WriteLine("Example Startup Task Completed..."); - } -} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/appsettings.Development.json b/Api.StartupTasks/Api.StartupTasks/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.StartupTasks/Api.StartupTasks/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/appsettings.Production.json b/Api.StartupTasks/Api.StartupTasks/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.StartupTasks/Api.StartupTasks/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/appsettings.Staging.json b/Api.StartupTasks/Api.StartupTasks/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.StartupTasks/Api.StartupTasks/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/appsettings.json b/Api.StartupTasks/Api.StartupTasks/appsettings.json deleted file mode 100644 index 053b76d1..00000000 --- a/Api.StartupTasks/Api.StartupTasks/appsettings.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - } - } -} \ No newline at end of file diff --git a/Api.StartupTasks/Dockerfile b/Api.StartupTasks/Dockerfile deleted file mode 100644 index 13ab1f4e..00000000 --- a/Api.StartupTasks/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.StartupTasks.dll"] \ No newline at end of file diff --git a/Api.StartupTasks/LICENSE b/Api.StartupTasks/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.StartupTasks/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.StartupTasks/README.md b/Api.StartupTasks/README.md deleted file mode 100644 index ed826734..00000000 --- a/Api.StartupTasks/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Api.StartupTasks - -> _Nano API application with startup tasks._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -After approximately 20 seconds, the startup task will complete, and the health check endpoint will report a status of `Healthy`. - -The following endpoint is available for testing. - -| Endpoint | Description | -| --------------------------------------------------- | -------------------------------------- | -| `http://localhost:8080/api/examples/startup-tasks` | Returns a simple `200 OK` response. | - -> 📖 Learn more about **[Nano Startup Tasks](https://github.com/Nano-Core/tree/master/Nano.Library/Nano.App#startup-tasks)**. - -## Configuration -There is no configuration required for startup tasks themselves. -The health-check configuration has just been added to observe that the `self` health-check reports `Healthy`. - -```json - "App": { - "HealthCheck": { - } - } -```` diff --git a/Api.StartupTasks/icon.png b/Api.StartupTasks/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.StaticFiles/.docker/docker-compose.yml b/Api.StaticFiles/.docker/docker-compose.yml deleted file mode 100644 index ceae04df..00000000 --- a/Api.StaticFiles/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.staticfiles: - image: api.staticfiles - hostname: api-staticfiles - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.StaticFiles - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.StaticFiles/.dockerignore b/Api.StaticFiles/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.StaticFiles/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.StaticFiles/.github/config/slack.yml b/Api.StaticFiles/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.StaticFiles/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.StaticFiles/.github/workflows/build-and-deploy.yml b/Api.StaticFiles/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 670fae49..00000000 --- a/Api.StaticFiles/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.StaticFiles - IMAGE_NAME: api.staticfiles - SERVICE_NAME: api-staticfiles - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.StaticFiles/.gitignore b/Api.StaticFiles/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.StaticFiles/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.StaticFiles/.kubernetes/autoscaler.yaml b/Api.StaticFiles/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.StaticFiles/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.StaticFiles/.kubernetes/configmap.yaml b/Api.StaticFiles/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.StaticFiles/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.StaticFiles/.kubernetes/deployment.yaml b/Api.StaticFiles/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.StaticFiles/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.StaticFiles/.kubernetes/service.yaml b/Api.StaticFiles/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.StaticFiles/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Properties/DoNotParallelize.cs b/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Tests.Api.StaticFiles.csproj b/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Tests.Api.StaticFiles.csproj deleted file mode 100644 index 312152db..00000000 --- a/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Tests.Api.StaticFiles.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj b/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles.sln b/Api.StaticFiles/Api.StaticFiles.sln deleted file mode 100644 index dd7885ad..00000000 --- a/Api.StaticFiles/Api.StaticFiles.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.StaticFiles.Models", "Api.StaticFiles.Models\Api.StaticFiles.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.StaticFiles", "Api.StaticFiles\Api.StaticFiles.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.StaticFiles", ".tests\Tests.Api.StaticFiles\Tests.Api.StaticFiles.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.StaticFiles/Api.StaticFiles/Api.StaticFiles.csproj b/Api.StaticFiles/Api.StaticFiles/Api.StaticFiles.csproj deleted file mode 100644 index 766773d6..00000000 --- a/Api.StaticFiles/Api.StaticFiles/Api.StaticFiles.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/Controllers/ExamplesController.cs b/Api.StaticFiles/Api.StaticFiles/Controllers/ExamplesController.cs deleted file mode 100644 index a6c62560..00000000 --- a/Api.StaticFiles/Api.StaticFiles/Controllers/ExamplesController.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; - -namespace Api.StaticFiles.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Static Files Action. - /// - /// The cancellation token. - /// A message. - /// Success. - [HttpGet] - [Route("static-files")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task StaticFilesAsync(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("static-files"); - } -} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local b/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local deleted file mode 100644 index ad327791..00000000 --- a/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.StaticFiles.dll"] \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/Program.cs b/Api.StaticFiles/Api.StaticFiles/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.StaticFiles/Api.StaticFiles/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.StaticFiles/Api.StaticFiles/Properties/InternalsVisibleTo.cs b/Api.StaticFiles/Api.StaticFiles/Properties/InternalsVisibleTo.cs deleted file mode 100644 index f35cb962..00000000 --- a/Api.StaticFiles/Api.StaticFiles/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.StaticFiles")] \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/appsettings.Development.json b/Api.StaticFiles/Api.StaticFiles/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.StaticFiles/Api.StaticFiles/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/appsettings.Production.json b/Api.StaticFiles/Api.StaticFiles/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.StaticFiles/Api.StaticFiles/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/appsettings.Staging.json b/Api.StaticFiles/Api.StaticFiles/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.StaticFiles/Api.StaticFiles/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/appsettings.json b/Api.StaticFiles/Api.StaticFiles/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.StaticFiles/Api.StaticFiles/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/wwwroot/fonts/open-sans-v44-latin-regular.woff2 b/Api.StaticFiles/Api.StaticFiles/wwwroot/fonts/open-sans-v44-latin-regular.woff2 deleted file mode 100644 index e2d3fa4ea21557e7ee10e08b42a82dd3de54ea4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18640 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

=pE2;YPyGP} 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%0DK88R_X{XRP@ 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 BR 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 WEaIVTB`mgga((~bV - - - - Static Files Test - - - - -

Static Files Test

- Test Image -

If you see the image and text in the custom font, static files are working!

- - \ No newline at end of file diff --git a/Api.StaticFiles/Dockerfile b/Api.StaticFiles/Dockerfile deleted file mode 100644 index ab17eafa..00000000 --- a/Api.StaticFiles/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.StaticFiles.dll"] \ No newline at end of file diff --git a/Api.StaticFiles/LICENSE b/Api.StaticFiles/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.StaticFiles/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.StaticFiles/README.md b/Api.StaticFiles/README.md deleted file mode 100644 index 79e676a1..00000000 --- a/Api.StaticFiles/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Api.StaticFiles - -> _Nano API application with static files._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -Static files have been added to the solution, including a font, an image, and an HTML file. -Run the application and navigate to [http://localhost:8080/index.html](http://localhost:8080/index.html) to verify that the static files are served correctly. - -The following endpoint is available for testing: - -| Endpoint | Description | -| -------------------------------------------------- | -------------------------------------- | -| `http://localhost:8080/api/examples/static-files` | Returns a simple `200 OK` response. | - -> 📖 Learn more about **[Nano Static Files](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#static-files)**. diff --git a/Api.StaticFiles/icon.png b/Api.StaticFiles/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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
- - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Storage.Azure/.docker/docker-compose.yml b/Api.Storage.Azure/.docker/docker-compose.yml deleted file mode 100644 index 881edf06..00000000 --- a/Api.Storage.Azure/.docker/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - api.storage.azure: - image: api.storage.azure - hostname: api-storage-azure - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Storage.Azure - dockerfile: "Dockerfile.Local" - networks: - - network - volumes: - - ./bin/nano-storage-azure:/mnt/nano-storage-azure - -networks: - network: - name: network - driver: bridge diff --git a/Api.Storage.Azure/.dockerignore b/Api.Storage.Azure/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Storage.Azure/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Storage.Azure/.github/config/slack.yml b/Api.Storage.Azure/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Storage.Azure/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Storage.Azure/.github/workflows/build-and-deploy.yml b/Api.Storage.Azure/.github/workflows/build-and-deploy.yml deleted file mode 100644 index f2ad4d85..00000000 --- a/Api.Storage.Azure/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,188 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Storage.Azure - IMAGE_NAME: api.storage.azure - SERVICE_NAME: api-storage-azure - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - STORAGE_SHARE_NAME: nano-storage-azure - STORAGE_CREDENTIALS_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_STORAGE_CREDENTIALS_ID || secrets.STAGING_STORAGE_CREDENTIALS_ID }} - STORAGE_CREDENTIALS_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_STORAGE_CREDENTIALS_SECRET || secrets.STAGING_STORAGE_CREDENTIALS_SECRET }} - STORAGE_SIZE: 1000 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Create Fileshare - shell: pwsh - run: | - $env:EXISTING_FILE_SHARE = sudo az storage share list --account-name $env:STORAGE_CREDENTIALS_ID --account-key $env:STORAGE_CREDENTIALS_SECRET --query "[?contains(name, '$env:STORAGE_SHARE_NAME')].[name]" -o tsv; - if ([string]::IsNullOrEmpty($env:EXISTING_FILE_SHARE)) - { - sudo az storage share create -n $env:STORAGE_SHARE_NAME --account-name $env:STORAGE_CREDENTIALS_ID --account-key $env:STORAGE_CREDENTIALS_SECRET --quota $env:STORAGE_SIZE; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - } - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Storage.Azure/.gitignore b/Api.Storage.Azure/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Storage.Azure/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Storage.Azure/.kubernetes/autoscaler.yaml b/Api.Storage.Azure/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Storage.Azure/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Storage.Azure/.kubernetes/configmap.yaml b/Api.Storage.Azure/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Storage.Azure/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Storage.Azure/.kubernetes/deployment.yaml b/Api.Storage.Azure/.kubernetes/deployment.yaml deleted file mode 100644 index a9ada48b..00000000 --- a/Api.Storage.Azure/.kubernetes/deployment.yaml +++ /dev/null @@ -1,107 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - env: - - name: Storage__Credentials__id - valueFrom: - secretKeyRef: - name: storage-account-secret - key: azurestorageaccountname - - name: Storage__Credentials__Secret - valueFrom: - secretKeyRef: - name: storage-account-secret - key: azurestorageaccountkey - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - - name: tmp - mountPath: /tmp - volumes: - - name: %SERVICE_NAME%-volume - azureFile: - secretName: storage-account-secret - shareName: %STORAGE_SHARE_NAME% - readOnly: false - - name: tmp - emptyDir: {} - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Storage.Azure/.kubernetes/service.yaml b/Api.Storage.Azure/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Storage.Azure/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Properties/DoNotParallelize.cs b/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Tests.Api.Storage.Azure.csproj b/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Tests.Api.Storage.Azure.csproj deleted file mode 100644 index f15b810a..00000000 --- a/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Tests.Api.Storage.Azure.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj b/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj deleted file mode 100644 index 2865dfbb..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure.sln b/Api.Storage.Azure/Api.Storage.Azure.sln deleted file mode 100644 index 12674d65..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure.sln +++ /dev/null @@ -1,147 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Storage.Azure.Models", "Api.Storage.Azure.Models\Api.Storage.Azure.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Storage.Azure", "Api.Storage.Azure\Api.Storage.Azure.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Storage.Azure", ".tests\Tests.Api.Storage.Azure\Tests.Api.Storage.Azure.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Azure", "..\..\Nano.Library\Nano.Storage.Azure\Nano.Storage.Azure.csproj", "{5DD1307B-16FA-5171-54CA-4F37F2638022}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage", "..\..\Nano.Library\Nano.Storage\Nano.Storage.csproj", "{24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {5DD1307B-16FA-5171-54CA-4F37F2638022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5DD1307B-16FA-5171-54CA-4F37F2638022}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5DD1307B-16FA-5171-54CA-4F37F2638022}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5DD1307B-16FA-5171-54CA-4F37F2638022}.Release|Any CPU.Build.0 = Release|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5DD1307B-16FA-5171-54CA-4F37F2638022} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Storage.Azure/Api.Storage.Azure/Api.Storage.Azure.csproj b/Api.Storage.Azure/Api.Storage.Azure/Api.Storage.Azure.csproj deleted file mode 100644 index a37eaf41..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/Api.Storage.Azure.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - 91ada628-9aa0-450e-9029-98671550a47f - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/Controllers/ExamplesController.cs b/Api.Storage.Azure/Api.Storage.Azure/Controllers/ExamplesController.cs deleted file mode 100644 index 7fdfbdb7..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/Controllers/ExamplesController.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Common.Consts; -using Nano.Storage.Abstractions; -using System.ComponentModel.DataAnnotations; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Nano.App.Api.Extensions; - -namespace Api.Storage.Azure.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IPathProvider pathProvider) : BaseController(logger) -{ - /// - /// Storage Azure Action. - /// - /// The file. - /// The token used when request is cancelled. - /// A message. - /// OK. - [HttpPost] - [Route("storage")] - [Consumes(HttpContentType.FORM)] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task StorageAzureAsync([Required]IFormFile file, CancellationToken cancellationToken = default) - { - var fileName = Path.GetFileName(file.FileName); - var savePath = Path.Combine(pathProvider.Root, fileName); - - await file - .SaveFileAsync(savePath, cancellationToken); - - return this.Ok("storage-azure"); - } -} \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local b/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local deleted file mode 100644 index eeb40cec..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Storage.Azure.dll"] \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/Program.cs b/Api.Storage.Azure/Api.Storage.Azure/Program.cs deleted file mode 100644 index a6e84c1e..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Api; -using Nano.Storage.Azure; -using Nano.Storage.Extensions; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoStorage(); - }) - .Build() - .Run(); diff --git a/Api.Storage.Azure/Api.Storage.Azure/Properties/InternalsVisibleTo.cs b/Api.Storage.Azure/Api.Storage.Azure/Properties/InternalsVisibleTo.cs deleted file mode 100644 index cc3881c2..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Storage.Azure")] \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Development.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Production.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Staging.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.json deleted file mode 100644 index bfbfee6d..00000000 --- a/Api.Storage.Azure/Api.Storage.Azure/appsettings.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - } - }, - "Storage": { - "ShareName": "nano-storage-azure", - "Credentials": { - "Id": null, - "Secret": null - }, - "HealthCheck": { - "UnhealthyStatus": "Degraded" - } - } -} \ No newline at end of file diff --git a/Api.Storage.Azure/Dockerfile b/Api.Storage.Azure/Dockerfile deleted file mode 100644 index 6b462057..00000000 --- a/Api.Storage.Azure/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Storage.Azure.dll"] \ No newline at end of file diff --git a/Api.Storage.Azure/LICENSE b/Api.Storage.Azure/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Storage.Azure/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Storage.Azure/README.md b/Api.Storage.Azure/README.md deleted file mode 100644 index a64706ca..00000000 --- a/Api.Storage.Azure/README.md +++ /dev/null @@ -1,155 +0,0 @@ -# Api.Storage.Azure - -> _Nano API application with azure storage._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This application demonstrates uploading a file and saving it to a mapped file share. -When running locally, files are **NOT** written to the Azure File Share. Instead, Docker mounts a local directory to simulate the file share. -Files are saved in `.docker/bin/`. - -A storage health check is configured to target the Azure File Share, but it requires valid credentials to be provided under `Storage.Credentials`. If the -credentials are omitted from the configuration, the application will still run, but the health-check will report `degraded`. - -Open [http://localhost:8080/healthz](http://localhost:8080/healthz) to view the storage health-check JSON response. - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#health-checks)**. - -The following endpoint is available for testing. - -| Endpoint | Description | -| --------------------------------------------------- | ------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/storage` | Returns a simple `200 OK` response. Saves the uploaded file to the fileshare. | - -> 📖 Learn more about **[Nano.Storage.Azure](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Storage.Azure)**. - -## Registration -The following storage provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(x => -{ - x.AddNanoStorage(); -}) -... -``` - -## Configuration -Add the storage configuration. - -```json -"Storage": { - "ShareName": "nano-storage-azure", - "Credentials": { - "Id": "id", - "Secret": "secret" - }, - "HealthCheck": { - "UnhealthyStatus": "Degraded" - } -} -``` - -Additionally, application health-checks have been enabled with the configuration. - -```json -"App": { - "HealthCheck": { - "EvaluationInterval": 10, - "FailureNotificationInterval": 60, - "MaximumHistoryEntriesPerEndpoint": 50 - } -} -``` - -## Docker Compose -Mapped the fileshare in `docker-compose.yml`. - -```yaml -docker - volumes: - - ./bin/nano-storage-azure:/mnt/nano-storage-azure -``` - -## Kubernetes -Added the volumes, volume mounts and secrets to the `deployment.yaml`. - -```json -spec: - template: - spec: - containers: - env: - - name: Storage__Credentials__Id - valueFrom: - secretKeyRef: - name: storage-account-secret - key: azurestorageaccountname - - name: Storage__Credentials__Secret - valueFrom: - secretKeyRef: - name: storage-account-secret - key: azurestorageaccountkey - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - volumes: - - name: tmp - emptyDir: {} - - name: %SERVICE_NAME%-volume - azureFile: - secretName: storage-account-secret - shareName: %STORAGE_SHARE_NAME% - readOnly: false -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - STORAGE_SHARE_NAME: nano-storage-azure - STORAGE_CREDENTIALS_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_STORAGE_CREDENTIALS_ID || secrets.STAGING_STORAGE_CREDENTIALS_ID }} - STORAGE_CREDENTIALS_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_STORAGE_CREDENTIALS_SECRET || secrets.STAGING_STORAGE_CREDENTIALS_SECRET }} - STORAGE_SIZE: 1000 -``` - -And add this step below as well, ensuring that the fileshare gets created before the application is deployed. - -```yaml -- name: Create Fileshare - shell: pwsh - run: | - $env:EXISTING_FILE_SHARE = sudo az storage share list --account-name $env:STORAGE_CREDENTIALS_ID --account-key $env:STORAGE_CREDENTIALS_SECRET --query "[?contains(name, '$env:STORAGE_SHARE_NAME')].[name]" -o tsv; - if ([string]::IsNullOrEmpty($env:EXISTING_FILE_SHARE)) - { - sudo az storage share create -n $env:STORAGE_SHARE_NAME --account-name $env:STORAGE_CREDENTIALS_ID --account-key $env:STORAGE_CREDENTIALS_SECRET --quota $env:STORAGE_SIZE; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - } -``` \ No newline at end of file diff --git a/Api.Storage.Azure/icon.png b/Api.Storage.Azure/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Storage.Local/.docker/docker-compose.yml b/Api.Storage.Local/.docker/docker-compose.yml deleted file mode 100644 index c33c1940..00000000 --- a/Api.Storage.Local/.docker/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - api.storage.local: - image: api.storage.local - hostname: api-storage-local - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Storage.Local - dockerfile: "Dockerfile.Local" - networks: - - network - volumes: - - ./bin/nano-storage-local:/mnt/nano-storage-local - -networks: - network: - name: network - driver: bridge diff --git a/Api.Storage.Local/.dockerignore b/Api.Storage.Local/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Storage.Local/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Storage.Local/.github/config/slack.yml b/Api.Storage.Local/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Storage.Local/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Storage.Local/.github/workflows/build-and-deploy.yml b/Api.Storage.Local/.github/workflows/build-and-deploy.yml deleted file mode 100644 index b5c5102d..00000000 --- a/Api.Storage.Local/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,185 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Storage.Local - IMAGE_NAME: api.storage.local - SERVICE_NAME: api-storage-local - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - STORAGE_SHARE_NAME: nano-storage-local - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/storage-storageclass.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-storageclass.tmp.yaml; - sudo kubectl apply -f .kubernetes/storage-storageclass.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/storage-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pvc.tmp.yaml; - sudo kubectl apply -f .kubernetes/storage-pvc.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Storage.Local/.gitignore b/Api.Storage.Local/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Storage.Local/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Storage.Local/.kubernetes/autoscaler.yaml b/Api.Storage.Local/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Storage.Local/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Storage.Local/.kubernetes/configmap.yaml b/Api.Storage.Local/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Storage.Local/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Storage.Local/.kubernetes/deployment.yaml b/Api.Storage.Local/.kubernetes/deployment.yaml deleted file mode 100644 index 9dd316cd..00000000 --- a/Api.Storage.Local/.kubernetes/deployment.yaml +++ /dev/null @@ -1,89 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - - name: tmp - mountPath: /tmp - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc - - name: tmp - emptyDir: {} - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Storage.Local/.kubernetes/service.yaml b/Api.Storage.Local/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Storage.Local/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Storage.Local/.kubernetes/storage-pvc.yaml b/Api.Storage.Local/.kubernetes/storage-pvc.yaml deleted file mode 100644 index 2285607e..00000000 --- a/Api.Storage.Local/.kubernetes/storage-pvc.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: %SERVICE_NAME%-pvc -spec: - accessModes: - - ReadWriteOnce - storageClassName: %SERVICE_NAME%-storage-class - resources: - requests: - storage: %STORAGE_SIZE% \ No newline at end of file diff --git a/Api.Storage.Local/.kubernetes/storage-storageclass.yaml b/Api.Storage.Local/.kubernetes/storage-storageclass.yaml deleted file mode 100644 index f9ccaa3e..00000000 --- a/Api.Storage.Local/.kubernetes/storage-storageclass.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: %SERVICE_NAME%-storage-class -provisioner: kubernetes.io/azure-disk -parameters: - storageaccounttype: Standard_LRS - kind: Managed -reclaimPolicy: Retain -volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Properties/DoNotParallelize.cs b/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Tests.Api.Storage.Local.csproj b/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Tests.Api.Storage.Local.csproj deleted file mode 100644 index 535b3efb..00000000 --- a/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Tests.Api.Storage.Local.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj b/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj deleted file mode 100644 index 8f47aa23..00000000 --- a/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj +++ /dev/null @@ -1,77 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local.sln b/Api.Storage.Local/Api.Storage.Local.sln deleted file mode 100644 index b4d234bc..00000000 --- a/Api.Storage.Local/Api.Storage.Local.sln +++ /dev/null @@ -1,149 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - .kubernetes\storage-pvc.yaml = .kubernetes\storage-pvc.yaml - .kubernetes\storage-storageclass.yaml = .kubernetes\storage-storageclass.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Storage.Local.Models", "Api.Storage.Local.Models\Api.Storage.Local.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Storage.Local", "Api.Storage.Local\Api.Storage.Local.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Storage.Local", ".tests\Tests.Api.Storage.Local\Tests.Api.Storage.Local.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage", "..\..\Nano.Library\Nano.Storage\Nano.Storage.csproj", "{24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Local", "..\..\Nano.Library\Nano.Storage.Local\Nano.Storage.Local.csproj", "{6D21CF50-D3ED-4D55-27D9-D426E661A5E4}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.Build.0 = Release|Any CPU - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Storage.Local/Api.Storage.Local/Api.Storage.Local.csproj b/Api.Storage.Local/Api.Storage.Local/Api.Storage.Local.csproj deleted file mode 100644 index d892da66..00000000 --- a/Api.Storage.Local/Api.Storage.Local/Api.Storage.Local.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - 9c0012fe-f61b-4cda-b051-a7219073bc53 - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/Controllers/ExamplesController.cs b/Api.Storage.Local/Api.Storage.Local/Controllers/ExamplesController.cs deleted file mode 100644 index 870442da..00000000 --- a/Api.Storage.Local/Api.Storage.Local/Controllers/ExamplesController.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.ComponentModel.DataAnnotations; -using System.IO; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.App.Api.Extensions; -using Nano.Common.Consts; -using Nano.Storage.Abstractions; - -namespace Api.Storage.Local.Controllers; - -/// -/// Controller with examples. -/// -/// The . -/// The . -public class ExamplesController(ILogger logger, IPathProvider pathProvider) : BaseController(logger) -{ - /// - /// Storage Local. - /// - /// The file. - /// The token used when request is cancelled. - /// A message. - /// OK. - [HttpPost] - [Route("storage")] - [Consumes(HttpContentType.FORM)] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task StorageLocalAsync([Required]IFormFile file, CancellationToken cancellationToken = default) - { - var fileName = Path.GetFileName(file.FileName); - var savePath = Path.Combine(pathProvider.Root, fileName); - - await file - .SaveFileAsync(savePath, cancellationToken); - - return this.Ok("storage-local"); - } -} \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local b/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local deleted file mode 100644 index 6512ea12..00000000 --- a/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Storage.Local.dll"] \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/Program.cs b/Api.Storage.Local/Api.Storage.Local/Program.cs deleted file mode 100644 index 5116914b..00000000 --- a/Api.Storage.Local/Api.Storage.Local/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Api; -using Nano.Storage.Extensions; -using Nano.Storage.Local; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(x => - { - x.AddNanoStorage(); - }) - .Build() - .Run(); diff --git a/Api.Storage.Local/Api.Storage.Local/Properties/InternalsVisibleTo.cs b/Api.Storage.Local/Api.Storage.Local/Properties/InternalsVisibleTo.cs deleted file mode 100644 index e4169829..00000000 --- a/Api.Storage.Local/Api.Storage.Local/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Storage.Local")] \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.Development.json b/Api.Storage.Local/Api.Storage.Local/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Storage.Local/Api.Storage.Local/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.Production.json b/Api.Storage.Local/Api.Storage.Local/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Storage.Local/Api.Storage.Local/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.Staging.json b/Api.Storage.Local/Api.Storage.Local/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Storage.Local/Api.Storage.Local/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.json b/Api.Storage.Local/Api.Storage.Local/appsettings.json deleted file mode 100644 index 6a2c9c46..00000000 --- a/Api.Storage.Local/Api.Storage.Local/appsettings.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "HealthCheck": { - } - }, - "Storage": { - "ShareName": "nano-storage-local", - "HealthCheck": { - "UnhealthyStatus": "UnHealthy" - } - } -} \ No newline at end of file diff --git a/Api.Storage.Local/Dockerfile b/Api.Storage.Local/Dockerfile deleted file mode 100644 index 657921b0..00000000 --- a/Api.Storage.Local/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Storage.Local.dll"] \ No newline at end of file diff --git a/Api.Storage.Local/LICENSE b/Api.Storage.Local/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Storage.Local/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Storage.Local/README.md b/Api.Storage.Local/README.md deleted file mode 100644 index 5cf3390e..00000000 --- a/Api.Storage.Local/README.md +++ /dev/null @@ -1,126 +0,0 @@ -# Api.Storage.Local - -> _Nano API application with local storage._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This application demonstrates uploading a file and saving it to a locally mapped fileshare. -Files are saved in `.docker/bin/`. - -Storage health-check has also been configured. -Open [http://localhost:8080/healthz](http://localhost:8080/healthz) to view the storage health-check in the JSON response. - -The following endpoint is available for testing: - -| Endpoint | Description | -| --------------------------------------------- | ------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/storage` | Returns a simple `200 OK` response. Saves the uploaded file to the fileshare. | - -> 📖 Learn more about **[Nano.Storage.Local](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Storage.Local)**. - -## Registration -The following storage has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(x => -{ - x.AddNanoStorage(); -}) -... -``` - -## Configuration -Configured the application with the necessary storage setup. - -```json -"Storage": { - "ShareName": "nano-storage-local", - "HealthCheck": { - "UnhealthyStatus": "Degraded" - } -} -``` - -Additionally, application health-checks have been enabled with the configuration. - -```json -"App": { - "HealthCheck": { - "EvaluationInterval": 10, - "FailureNotificationInterval": 60, - "MaximumHistoryEntriesPerEndpoint": 50 - } -} -``` - -## Docker Compose -Mapped the fileshare in `docker-compose.yml`. - -```yaml -docker - volumes: - - ./bin/nano-storage-local:/mnt/nano-storage-local -``` - -## Kubernetes -Added two additional kubernetes templates, `storage-storageclass.yaml` and `storage-pvc.yaml`, for dynamically manage and creating the local fileshare. - -Also, updated `deployment.yaml` adding the volumes and volume mounts. - -```json -spec: - template: - spec: - containers: - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - - name: tmp - mountPath: /tmp - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc - - name: tmp - emptyDir: {} -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - STORAGE_SHARE_NAME: nano-storage-local - STORAGE_SIZE: 1000 -``` - -Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Api.Storage.Local/icon.png b/Api.Storage.Local/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.TimeZone/.docker/docker-compose.yml b/Api.TimeZone/.docker/docker-compose.yml deleted file mode 100644 index f9debe0d..00000000 --- a/Api.TimeZone/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.timezone: - image: api.timezone - hostname: api-timezone - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.TimeZone - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.TimeZone/.dockerignore b/Api.TimeZone/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.TimeZone/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.TimeZone/.github/config/slack.yml b/Api.TimeZone/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.TimeZone/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.TimeZone/.github/workflows/build-and-deploy.yml b/Api.TimeZone/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 85c1a63e..00000000 --- a/Api.TimeZone/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.TimeZone - IMAGE_NAME: api.timezone - SERVICE_NAME: api-timezone - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.TimeZone/.gitignore b/Api.TimeZone/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.TimeZone/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.TimeZone/.kubernetes/autoscaler.yaml b/Api.TimeZone/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.TimeZone/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.TimeZone/.kubernetes/configmap.yaml b/Api.TimeZone/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.TimeZone/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.TimeZone/.kubernetes/deployment.yaml b/Api.TimeZone/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.TimeZone/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.TimeZone/.kubernetes/service.yaml b/Api.TimeZone/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.TimeZone/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.TimeZone/.tests/Tests.Api.TimeZone/Properties/DoNotParallelize.cs b/Api.TimeZone/.tests/Tests.Api.TimeZone/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.TimeZone/.tests/Tests.Api.TimeZone/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.TimeZone/.tests/Tests.Api.TimeZone/Tests.Api.TimeZone.csproj b/Api.TimeZone/.tests/Tests.Api.TimeZone/Tests.Api.TimeZone.csproj deleted file mode 100644 index f3c341ee..00000000 --- a/Api.TimeZone/.tests/Tests.Api.TimeZone/Tests.Api.TimeZone.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj b/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone.sln b/Api.TimeZone/Api.TimeZone.sln deleted file mode 100644 index 8ba99efb..00000000 --- a/Api.TimeZone/Api.TimeZone.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.TimeZone.Models", "Api.TimeZone.Models\Api.TimeZone.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.TimeZone", "Api.TimeZone\Api.TimeZone.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.TimeZone", ".tests\Tests.Api.TimeZone\Tests.Api.TimeZone.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj b/Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj deleted file mode 100644 index 36aee63d..00000000 --- a/Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs b/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs deleted file mode 100644 index f41edbdb..00000000 --- a/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using Api.TimeZone.Controllers.Requests; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Vivet.AspNetCore.RequestTimeZone; - -namespace Api.TimeZone.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Time Zone GET Action. - /// - /// The date time. - /// The cancellation token. - /// An object showing different date times. - /// Success. - [HttpGet] - [Route("timezone")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task TimeZoneAsync([FromQuery][Required]DateTimeOffset dateTime, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - var clientRequestDateTime = TimeZoneInfo.ConvertTime(dateTime, DateTimeInfo.TimeZone.Value!); - - return this.Ok(new - { - RequestDateTimeLocal = clientRequestDateTime, - ServerRecievedUtc = dateTime.UtcDateTime, - ResponseDateTimeLocal = dateTime, - DateTimeInfoNow = DateTimeInfo.Now, - DateTimeInfoUtcNow = DateTimeInfo.UtcNow.UtcDateTime - }); - } - - /// - /// Time Zone POST Action. - /// - /// The date time request. - /// The cancellation token. - /// An object showing different date times. - /// Success. - [HttpPost] - [Route("timezone")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task TimeZoneAsync([FromBody][Required]DateTimeRequest request, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - var clientRequestDateTime = TimeZoneInfo.ConvertTime(request.DateTime, DateTimeInfo.TimeZone.Value!); - - return this.Ok(new - { - RequestDateTimeLocal = clientRequestDateTime, - ServerRecievedUtc = request.DateTime.UtcDateTime, - ResponseDateTimeLocal = request.DateTime, - DateTimeInfoNow = DateTimeInfo.Now, - DateTimeInfoUtcNow = DateTimeInfo.UtcNow.UtcDateTime - }); - } -} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Controllers/Requests/DateTimeRequest.cs b/Api.TimeZone/Api.TimeZone/Controllers/Requests/DateTimeRequest.cs deleted file mode 100644 index b5f7b723..00000000 --- a/Api.TimeZone/Api.TimeZone/Controllers/Requests/DateTimeRequest.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; - -namespace Api.TimeZone.Controllers.Requests; - -/// -/// Date Time Request. -/// -public class DateTimeRequest -{ - /// - /// Date Time. - /// - [Required] - public virtual DateTimeOffset DateTime { get; set; } -} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Dockerfile.Local b/Api.TimeZone/Api.TimeZone/Dockerfile.Local deleted file mode 100644 index 0ab8570b..00000000 --- a/Api.TimeZone/Api.TimeZone/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.TimeZone.dll"] \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Program.cs b/Api.TimeZone/Api.TimeZone/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.TimeZone/Api.TimeZone/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.TimeZone/Api.TimeZone/Properties/InternalsVisibleTo.cs b/Api.TimeZone/Api.TimeZone/Properties/InternalsVisibleTo.cs deleted file mode 100644 index d871e5fc..00000000 --- a/Api.TimeZone/Api.TimeZone/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.TimeZone")] \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/appsettings.Development.json b/Api.TimeZone/Api.TimeZone/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.TimeZone/Api.TimeZone/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/appsettings.Production.json b/Api.TimeZone/Api.TimeZone/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.TimeZone/Api.TimeZone/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/appsettings.Staging.json b/Api.TimeZone/Api.TimeZone/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.TimeZone/Api.TimeZone/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/appsettings.json b/Api.TimeZone/Api.TimeZone/appsettings.json deleted file mode 100644 index 9026e1cb..00000000 --- a/Api.TimeZone/Api.TimeZone/appsettings.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - }, - "TimeZone": { - "DefaultTimeZone": "UTC" - } - } -} \ No newline at end of file diff --git a/Api.TimeZone/Dockerfile b/Api.TimeZone/Dockerfile deleted file mode 100644 index f7c7188e..00000000 --- a/Api.TimeZone/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.TimeZone.dll"] \ No newline at end of file diff --git a/Api.TimeZone/LICENSE b/Api.TimeZone/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.TimeZone/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.TimeZone/README.md b/Api.TimeZone/README.md deleted file mode 100644 index 7734094e..00000000 --- a/Api.TimeZone/README.md +++ /dev/null @@ -1,52 +0,0 @@ -# Api.TimeZone - -> _Nano API application with request timezone._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -The example demonstrates using request time zone in a Nano application. - -Both the GET and POST endpoints in the controller return a response in the following format: - -```json -{ - "RequestDateTimeLocal": "2026-02-08T14:23:45+03:00", // The date-time sent by the client in the request - "ServerReceivedUtc": "2026-02-08T11:23:45Z", // The UTC date-time when the server received the request - "ResponseDateTimeLocal": "2026-02-08T14:23:45+03:00", // The date-time returned to the client, adjusted to the requested timezone - "DateTimeInfoNow": "2026-02-10T11:40:37+03:00", // The current local date-time on the server - "DateTimeInfoUtcNow": "2026-02-10T08:40:37.7265135Z" // The current UTC date-time on the server -} -``` - -The following endpoint is available for testing. - -| Endpoint | Description | -| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | -| `http://localhost:8080/api/examples/timezone` (GET,POST) | Returns a `200 OK` response with various `DateTimeOffset` properties illustrating the date-time timezone conversions. | - -> 📖 Learn more about **[Nano TimeZone](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#timezone)**. - -## Configuration -```json -"App": { - "TimeZone": { - "DefaultTimeZone": "UTC" - } -} -``` diff --git a/Api.TimeZone/icon.png b/Api.TimeZone/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.Versioning/.docker/docker-compose.yml b/Api.Versioning/.docker/docker-compose.yml deleted file mode 100644 index c3a3fc80..00000000 --- a/Api.Versioning/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.versioning: - image: api.versioning - hostname: api-versioning - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Versioning - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.Versioning/.dockerignore b/Api.Versioning/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.Versioning/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.Versioning/.github/config/slack.yml b/Api.Versioning/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.Versioning/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Versioning/.github/workflows/build-and-deploy.yml b/Api.Versioning/.github/workflows/build-and-deploy.yml deleted file mode 100644 index a565aa0a..00000000 --- a/Api.Versioning/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Versioning - IMAGE_NAME: api.versioning - SERVICE_NAME: api-versioning - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Versioning/.gitignore b/Api.Versioning/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.Versioning/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Versioning/.kubernetes/autoscaler.yaml b/Api.Versioning/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.Versioning/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Versioning/.kubernetes/configmap.yaml b/Api.Versioning/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.Versioning/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Versioning/.kubernetes/deployment.yaml b/Api.Versioning/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.Versioning/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.Versioning/.kubernetes/service.yaml b/Api.Versioning/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.Versioning/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.Versioning/.tests/Tests.Api.Versioning/Properties/DoNotParallelize.cs b/Api.Versioning/.tests/Tests.Api.Versioning/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.Versioning/.tests/Tests.Api.Versioning/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Versioning/.tests/Tests.Api.Versioning/Tests.Api.Versioning.csproj b/Api.Versioning/.tests/Tests.Api.Versioning/Tests.Api.Versioning.csproj deleted file mode 100644 index f652aec3..00000000 --- a/Api.Versioning/.tests/Tests.Api.Versioning/Tests.Api.Versioning.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj b/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning.sln b/Api.Versioning/Api.Versioning.sln deleted file mode 100644 index 1e775d1c..00000000 --- a/Api.Versioning/Api.Versioning.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Versioning.Models", "Api.Versioning.Models\Api.Versioning.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Versioning", "Api.Versioning\Api.Versioning.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Versioning", ".tests\Tests.Api.Versioning\Tests.Api.Versioning.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.Versioning/Api.Versioning/Api.Versioning.csproj b/Api.Versioning/Api.Versioning/Api.Versioning.csproj deleted file mode 100644 index 205fc2c7..00000000 --- a/Api.Versioning/Api.Versioning/Api.Versioning.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/Controllers/ExamplesController.cs b/Api.Versioning/Api.Versioning/Controllers/ExamplesController.cs deleted file mode 100644 index 075a8173..00000000 --- a/Api.Versioning/Api.Versioning/Controllers/ExamplesController.cs +++ /dev/null @@ -1,51 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace Api.Versioning.Controllers; - -/// -/// Controller with examples. -/// -/// The . -[ApiVersion("1.0")] -[ApiVersion("2.0")] -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Version 1.0 (default) Action. - /// - /// The cancellation token. - /// A message. - /// Ok. - [HttpGet] - [Route("versioning")] - [MapToApiVersion("1.0")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task Version10Async(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("v1.0"); - } - - /// - /// Version 2.0 Action. - /// - /// The cancellation token. - /// A message. - /// Ok. - [HttpGet] - [Route("versioning")] - [MapToApiVersion("2.0")] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task Version20Async(CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("v2.0"); - } -} \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/Dockerfile.Local b/Api.Versioning/Api.Versioning/Dockerfile.Local deleted file mode 100644 index 273713a4..00000000 --- a/Api.Versioning/Api.Versioning/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Versioning.dll"] \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/Program.cs b/Api.Versioning/Api.Versioning/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.Versioning/Api.Versioning/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.Versioning/Api.Versioning/Properties/InternalsVisibleTo.cs b/Api.Versioning/Api.Versioning/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 75c29478..00000000 --- a/Api.Versioning/Api.Versioning/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Versioning")] \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/appsettings.Development.json b/Api.Versioning/Api.Versioning/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Versioning/Api.Versioning/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/appsettings.Production.json b/Api.Versioning/Api.Versioning/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Versioning/Api.Versioning/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/appsettings.Staging.json b/Api.Versioning/Api.Versioning/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.Versioning/Api.Versioning/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/appsettings.json b/Api.Versioning/Api.Versioning/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api.Versioning/Api.Versioning/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api.Versioning/Dockerfile b/Api.Versioning/Dockerfile deleted file mode 100644 index 079b95c9..00000000 --- a/Api.Versioning/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Versioning.dll"] \ No newline at end of file diff --git a/Api.Versioning/LICENSE b/Api.Versioning/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.Versioning/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.Versioning/README.md b/Api.Versioning/README.md deleted file mode 100644 index e91f0bd4..00000000 --- a/Api.Versioning/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Api.Versioning - -> _Nano API application with versioning._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -The controller is annotated with `[ApiVersion("1.0")]` and `[ApiVersion("2.0")]`, and contains two actions, each mapped to a specific version -using `[MapToApiVersion("1.0")]` and `[MapToApiVersion("2.0")]`, respectively. - -Observe the response headers `Api-Supported-Version`, containing all versions supported by the application, and the `Api-Version` showing the requested API version -by the client, or the default version if omitted from the request. - -The following endpoint is available for testing. - -| Endpoint | Description | -| ----------------------------------------------------- | ----------------------------------------------------- | -| `http://localhost:8080/api/v1.0/examples/versioning` | Returns a `200 OK` response with the message `v1.0`. | -| `http://localhost:8080/api/v2.0/examples/versioning` | Returns a `200 OK` response with the message `v2.0`. | - -> 📖 Learn more about **[Nano Versioning](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#versioning)**. diff --git a/Api.Versioning/icon.png b/Api.Versioning/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api.VirusScan/.docker/docker-compose.yml b/Api.VirusScan/.docker/docker-compose.yml deleted file mode 100644 index 0d5b74ed..00000000 --- a/Api.VirusScan/.docker/docker-compose.yml +++ /dev/null @@ -1,27 +0,0 @@ -services: - api.virusscan: - image: api.virusscan - hostname: api-virusscan - restart: on-failure - ports: - - 8080:8080 - depends_on: - - clamav - build: - context: ../Api.VirusScan - dockerfile: "Dockerfile.Local" - networks: - - network - - clamav: - image: clamav/clamav - hostname: clamav - ports: - - 3310:3310 - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api.VirusScan/.dockerignore b/Api.VirusScan/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api.VirusScan/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api.VirusScan/.github/config/slack.yml b/Api.VirusScan/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api.VirusScan/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.VirusScan/.github/workflows/build-and-deploy.yml b/Api.VirusScan/.github/workflows/build-and-deploy.yml deleted file mode 100644 index b1c3e79f..00000000 --- a/Api.VirusScan/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.VirusScan - IMAGE_NAME: api.virusscan - SERVICE_NAME: api-virusscan - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.VirusScan/.gitignore b/Api.VirusScan/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api.VirusScan/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.VirusScan/.kubernetes/autoscaler.yaml b/Api.VirusScan/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api.VirusScan/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.VirusScan/.kubernetes/configmap.yaml b/Api.VirusScan/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api.VirusScan/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.VirusScan/.kubernetes/deployment.yaml b/Api.VirusScan/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api.VirusScan/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api.VirusScan/.kubernetes/service.yaml b/Api.VirusScan/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api.VirusScan/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api.VirusScan/.tests/Tests.Api.VirusScan/Properties/DoNotParallelize.cs b/Api.VirusScan/.tests/Tests.Api.VirusScan/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api.VirusScan/.tests/Tests.Api.VirusScan/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.VirusScan/.tests/Tests.Api.VirusScan/Tests.Api.VirusScan.csproj b/Api.VirusScan/.tests/Tests.Api.VirusScan/Tests.Api.VirusScan.csproj deleted file mode 100644 index bc2d2085..00000000 --- a/Api.VirusScan/.tests/Tests.Api.VirusScan/Tests.Api.VirusScan.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj b/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan.sln b/Api.VirusScan/Api.VirusScan.sln deleted file mode 100644 index 6eacc515..00000000 --- a/Api.VirusScan/Api.VirusScan.sln +++ /dev/null @@ -1,140 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.VirusScan.Models", "Api.VirusScan.Models\Api.VirusScan.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.VirusScan", "Api.VirusScan\Api.VirusScan.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.VirusScan", ".tests\Tests.Api.VirusScan\Tests.Api.VirusScan.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vivet.AspNetCore.RequestVirusScan", "..\..\..\Vivet\Vivet.AspNetCore\Vivet.AspNetCore.RequestVirusScan\Vivet.AspNetCore.RequestVirusScan.csproj", "{04A79939-6413-2CB5-D3FA-974BBBCD8F7B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {04A79939-6413-2CB5-D3FA-974BBBCD8F7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {04A79939-6413-2CB5-D3FA-974BBBCD8F7B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {04A79939-6413-2CB5-D3FA-974BBBCD8F7B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {04A79939-6413-2CB5-D3FA-974BBBCD8F7B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {04A79939-6413-2CB5-D3FA-974BBBCD8F7B} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api.VirusScan/Api.VirusScan/Api.VirusScan.csproj b/Api.VirusScan/Api.VirusScan/Api.VirusScan.csproj deleted file mode 100644 index 2046e5df..00000000 --- a/Api.VirusScan/Api.VirusScan/Api.VirusScan.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/Controllers/ExamplesController.cs b/Api.VirusScan/Api.VirusScan/Controllers/ExamplesController.cs deleted file mode 100644 index 02094acc..00000000 --- a/Api.VirusScan/Api.VirusScan/Controllers/ExamplesController.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Nano.App.Api.Controllers; -using Nano.Common.Consts; -using System.ComponentModel.DataAnnotations; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace Api.VirusScan.Controllers; - -/// -/// Controller with examples. -/// -/// The . -public class ExamplesController(ILogger logger) : BaseController(logger) -{ - /// - /// Virus Scan Action. - /// - /// The file. - /// The token used when request is cancelled. - /// A message. - /// OK. - [HttpPost] - [Route("virus-scan")] - [Consumes(HttpContentType.FORM)] - [ProducesResponseType((int)HttpStatusCode.OK)] - public virtual async Task VirusScanAsync([Required]IFormFile file, CancellationToken cancellationToken = default) - { - await Task.CompletedTask; - - return this.Ok("no virus"); - } -} \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/Dockerfile.Local b/Api.VirusScan/Api.VirusScan/Dockerfile.Local deleted file mode 100644 index bcc73a22..00000000 --- a/Api.VirusScan/Api.VirusScan/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.VirusScan.dll"] \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/Program.cs b/Api.VirusScan/Api.VirusScan/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api.VirusScan/Api.VirusScan/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api.VirusScan/Api.VirusScan/Properties/InternalsVisibleTo.cs b/Api.VirusScan/Api.VirusScan/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 663ae613..00000000 --- a/Api.VirusScan/Api.VirusScan/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.VirusScan")] \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/appsettings.Development.json b/Api.VirusScan/Api.VirusScan/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.VirusScan/Api.VirusScan/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/appsettings.Production.json b/Api.VirusScan/Api.VirusScan/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.VirusScan/Api.VirusScan/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/appsettings.Staging.json b/Api.VirusScan/Api.VirusScan/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api.VirusScan/Api.VirusScan/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/appsettings.json b/Api.VirusScan/Api.VirusScan/appsettings.json deleted file mode 100644 index e3e6bc45..00000000 --- a/Api.VirusScan/Api.VirusScan/appsettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - }, - }, - "HealthCheck": { - }, - "VirusScan": { - "Host": "clamav", - "Port": 3310, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } - } -} \ No newline at end of file diff --git a/Api.VirusScan/Dockerfile b/Api.VirusScan/Dockerfile deleted file mode 100644 index 6fde6c7c..00000000 --- a/Api.VirusScan/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.VirusScan.dll"] \ No newline at end of file diff --git a/Api.VirusScan/LICENSE b/Api.VirusScan/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api.VirusScan/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api.VirusScan/README.md b/Api.VirusScan/README.md deleted file mode 100644 index 68a8722e..00000000 --- a/Api.VirusScan/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Api.VirusScan - -> _Nano API application with virus scanning._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) - -## Summary -This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller -that inherits from the top-level Nano `BaseController`. - -This example demonstrates using virus scan for all uploaded files in a Nano application. - -An virus scan health check is configured. -Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. - -> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#health-checks)** - -Invoke the endpoint. To test virus detection, you can use the EICAR test files -available here: [https://www.eicar.org/download-anti-malware-testfile](https://www.eicar.org/download-anti-malware-testfile). - -The following endpoint is available for testing. - -| Endpoint | Description | -| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -| `http://localhost:8080/api/examples/virus-scan` | Returns a `200 OK` response if there is no virus in the file, and otherwise a `500 ERROR` with the found virus name in the error message. | - -> 📖 Learn more about **[Nano Virus Scan](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#virus-scan)**. - -## Configuration -```json -"App": { - "VirusScan": { - "Host": "clamav", - "Port": 3310, - "HealthCheck": { - "UnhealthyStatus": "Unhealthy" - } - } -} -``` - -## Docker Compose -Added `ClamAV` dependency to the `docker-compose.yml` - -```yaml -services: - api.virusscan: - depends_on: - - clamav - - clamav: - image: clamav/clamav - hostname: clamav - ports: - - 3310:3310 - networks: - - network -``` diff --git a/Api.VirusScan/icon.png b/Api.VirusScan/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Api._Blank/.docker/docker-compose.yml b/Api._Blank/.docker/docker-compose.yml deleted file mode 100644 index a4aabf90..00000000 --- a/Api._Blank/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - api.blank: - image: api.blank - hostname: api-blank - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Api.Blank - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Api._Blank/.dockerignore b/Api._Blank/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Api._Blank/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Api._Blank/.github/config/slack.yml b/Api._Blank/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Api._Blank/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api._Blank/.github/workflows/build-and-deploy.yml b/Api._Blank/.github/workflows/build-and-deploy.yml deleted file mode 100644 index f90f9dda..00000000 --- a/Api._Blank/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Api.Blank - IMAGE_NAME: api.blank - SERVICE_NAME: api-blank - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api._Blank/.gitignore b/Api._Blank/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Api._Blank/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api._Blank/.kubernetes/autoscaler.yaml b/Api._Blank/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Api._Blank/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api._Blank/.kubernetes/configmap.yaml b/Api._Blank/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Api._Blank/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api._Blank/.kubernetes/deployment.yaml b/Api._Blank/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Api._Blank/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Api._Blank/.kubernetes/service.yaml b/Api._Blank/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Api._Blank/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Api._Blank/.tests/Tests.Api.Blank/Properties/DoNotParallelize.cs b/Api._Blank/.tests/Tests.Api.Blank/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Api._Blank/.tests/Tests.Api.Blank/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api._Blank/.tests/Tests.Api.Blank/Tests.Api.Blank.csproj b/Api._Blank/.tests/Tests.Api.Blank/Tests.Api.Blank.csproj deleted file mode 100644 index 100b3cf2..00000000 --- a/Api._Blank/.tests/Tests.Api.Blank/Tests.Api.Blank.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj b/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj deleted file mode 100644 index c33a8e9a..00000000 --- a/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Api._Blank/Api.Blank.sln b/Api._Blank/Api.Blank.sln deleted file mode 100644 index 37172573..00000000 --- a/Api._Blank/Api.Blank.sln +++ /dev/null @@ -1,133 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Blank.Models", "Api.Blank.Models\Api.Blank.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Blank", "Api.Blank\Api.Blank.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Blank", ".tests\Tests.Api.Blank\Tests.Api.Blank.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Api._Blank/Api.Blank/Api.Blank.csproj b/Api._Blank/Api.Blank/Api.Blank.csproj deleted file mode 100644 index 638806ab..00000000 --- a/Api._Blank/Api.Blank/Api.Blank.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Api._Blank/Api.Blank/Dockerfile.Local b/Api._Blank/Api.Blank/Dockerfile.Local deleted file mode 100644 index 9c84943b..00000000 --- a/Api._Blank/Api.Blank/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Api.Blank.dll"] \ No newline at end of file diff --git a/Api._Blank/Api.Blank/Program.cs b/Api._Blank/Api.Blank/Program.cs deleted file mode 100644 index 32865375..00000000 --- a/Api._Blank/Api.Blank/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Api; - -NanoApiApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); diff --git a/Api._Blank/Api.Blank/Properties/InternalsVisibleTo.cs b/Api._Blank/Api.Blank/Properties/InternalsVisibleTo.cs deleted file mode 100644 index b99fa9a8..00000000 --- a/Api._Blank/Api.Blank/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Api.Blank")] \ No newline at end of file diff --git a/Api._Blank/Api.Blank/appsettings.Development.json b/Api._Blank/Api.Blank/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api._Blank/Api.Blank/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api._Blank/Api.Blank/appsettings.Production.json b/Api._Blank/Api.Blank/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api._Blank/Api.Blank/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api._Blank/Api.Blank/appsettings.Staging.json b/Api._Blank/Api.Blank/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Api._Blank/Api.Blank/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Api._Blank/Api.Blank/appsettings.json b/Api._Blank/Api.Blank/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Api._Blank/Api.Blank/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Api._Blank/Dockerfile b/Api._Blank/Dockerfile deleted file mode 100644 index c06b6792..00000000 --- a/Api._Blank/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Api.Blank.dll"] \ No newline at end of file diff --git a/Api._Blank/LICENSE b/Api._Blank/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Api._Blank/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Api._Blank/README.md b/Api._Blank/README.md deleted file mode 100644 index caafa49d..00000000 --- a/Api._Blank/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Api.Blank - -> _Minimal (blank) Nano API application._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application represents the most minimal (blank) Nano API application setup. - -Its purpose is to demonstrate the required boilerplate, file structure, and general configuration needed to create a Nano API application. -The application itself is intentionally minimal and does not expose HTTP endpoints or configure additional features. Instead, it serves as a baseline -from which all other API lessons and examples are built. - -It is recommended to review this application first to understand how a Nano API application is generally structured and to become familiar with the purpose of the core building blocks -used in the boilerplate. - -> 📖 Learn more about **[Nano Solution Composition](https://github.com/Nano-Core/Nano.Library#solution-composition)**. diff --git a/Api._Blank/icon.png b/Api._Blank/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 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 - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.CustomConfigSection/.docker/docker-compose.yml b/Console.CustomConfigSection/.docker/docker-compose.yml deleted file mode 100644 index f055dd68..00000000 --- a/Console.CustomConfigSection/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.customconfigsection: - image: console.customconfigsection - restart: on-failure - build: - context: ../Console.CustomConfigSection - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.CustomConfigSection/.dockerignore b/Console.CustomConfigSection/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.CustomConfigSection/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.CustomConfigSection/.github/config/slack.yml b/Console.CustomConfigSection/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.CustomConfigSection/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml b/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 0dd3407a..00000000 --- a/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.CustomConfigSection - IMAGE_NAME: console.customconfigsection - SERVICE_NAME: console-customconfigsection - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.CustomConfigSection/.gitignore b/Console.CustomConfigSection/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.CustomConfigSection/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.CustomConfigSection/.kubernetes/configmap.yaml b/Console.CustomConfigSection/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.CustomConfigSection/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.CustomConfigSection/.kubernetes/cronjob.yaml b/Console.CustomConfigSection/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.CustomConfigSection/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Properties/DoNotParallelize.cs b/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Tests.Console.CustomConfigSection.csproj b/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Tests.Console.CustomConfigSection.csproj deleted file mode 100644 index 352e3917..00000000 --- a/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Tests.Console.CustomConfigSection.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.CustomConfigSection/Console.CustomConfigSection.sln b/Console.CustomConfigSection/Console.CustomConfigSection.sln deleted file mode 100644 index d0a81346..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection.sln +++ /dev/null @@ -1,123 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 d18.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.CustomConfigSection", "Console.CustomConfigSection\Console.CustomConfigSection.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.CustomConfigSection", ".tests\Tests.Console.CustomConfigSection\Tests.Console.CustomConfigSection.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Config/CustomOptions.cs b/Console.CustomConfigSection/Console.CustomConfigSection/Config/CustomOptions.cs deleted file mode 100644 index 4ecca56e..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/Config/CustomOptions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace Console.CustomConfigSection.Config; - -/// -/// Custom Options. -/// -public class CustomOptions -{ - internal static string SectionName => "Custom"; - - /// - /// Value. - /// - [Required] - public virtual string Value { get; set; } = null!; -} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Console.CustomConfigSection.csproj b/Console.CustomConfigSection/Console.CustomConfigSection/Console.CustomConfigSection.csproj deleted file mode 100644 index e0dfc0c1..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/Console.CustomConfigSection.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Dockerfile.Local b/Console.CustomConfigSection/Console.CustomConfigSection/Dockerfile.Local deleted file mode 100644 index f80be018..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Program.cs b/Console.CustomConfigSection/Console.CustomConfigSection/Program.cs deleted file mode 100644 index 1acc2bdd..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Console.CustomConfigSection.Config; -using Nano.App.Console; -using Nano.Common.Config.Extensions; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoConfigSection(CustomOptions.SectionName, out _); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Properties/InternalsVisibleTo.cs b/Console.CustomConfigSection/Console.CustomConfigSection/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 58489958..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.CustomConfigSection")] \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Workers/ExampleWorker.cs b/Console.CustomConfigSection/Console.CustomConfigSection/Workers/ExampleWorker.cs deleted file mode 100644 index 146982d8..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/Workers/ExampleWorker.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Console.CustomConfigSection.Config; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Nano.App.Console.Workers; - -namespace Console.CustomConfigSection.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IOptionsMonitor customOptions) : BaseWorker(logger) -{ - private readonly IOptionsMonitor customOptions = customOptions ?? throw new ArgumentNullException(nameof(customOptions)); - - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.CompletedTask; - - System.Console.WriteLine($"Custom Config Section Value: '{this.customOptions.CurrentValue.Value}'"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Development.json b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Production.json b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Staging.json b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.json b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.json deleted file mode 100644 index a0d63f9c..00000000 --- a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Custom": { - "Value": "custom-value" - } -} \ No newline at end of file diff --git a/Console.CustomConfigSection/Dockerfile b/Console.CustomConfigSection/Dockerfile deleted file mode 100644 index 14f95f28..00000000 --- a/Console.CustomConfigSection/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Console.CustomConfigSection/README.md b/Console.CustomConfigSection/README.md deleted file mode 100644 index 1e3691ff..00000000 --- a/Console.CustomConfigSection/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Console.CustomConfigSection - -> _Nano Console application with custom configuration section._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Configuration](#configuration) -* [Registration](#registration) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates a worker that uses a custom configuration section. - -Run the application to see services and hosted services being resolved, and observe the console output generated by the `ExampleWorker` -and the injected `CustomOptions` implementation. - -> 📖 Learn more about **[Nano Custom Configuration Sections](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api#custom-configuration-section)**. - -## Configuration -A custom configuration section has been added to `appsettings.json`: - -```json -"Custom": { - "Value": "custom-value" -} -``` - -## Registration -An option class matching the structure of the configuration has been implemented. - -```csharp -public class CustomOptions -{ - internal static string SectionName => "Custom"; - - [Required] - public virtual string Value { get; set; } = null!; -} -``` - -Finally, the options model has been registered with the section in startup, as shown below. - -```csharp -... -.ConfigureServices(x => -{ - x.AddNanoConfigSection(CustomOptions.SectionName, out _); -}) -... -``` diff --git a/Console.CustomService/.docker/docker-compose.dcproj b/Console.CustomService/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.CustomService/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.CustomService/.docker/docker-compose.yml b/Console.CustomService/.docker/docker-compose.yml deleted file mode 100644 index e05f251f..00000000 --- a/Console.CustomService/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.customservice: - image: console.customservice - restart: on-failure - build: - context: ../Console.CustomService - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.CustomService/.dockerignore b/Console.CustomService/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.CustomService/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.CustomService/.github/config/slack.yml b/Console.CustomService/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.CustomService/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.CustomService/.github/workflows/build-and-deploy.yml b/Console.CustomService/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 60592326..00000000 --- a/Console.CustomService/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.CustomService - IMAGE_NAME: console.customservice - SERVICE_NAME: console-customservice - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.CustomService/.gitignore b/Console.CustomService/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.CustomService/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.CustomService/.kubernetes/configmap.yaml b/Console.CustomService/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.CustomService/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.CustomService/.kubernetes/cronjob.yaml b/Console.CustomService/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.CustomService/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.CustomService/.tests/Tests.Console.CustomService/Properties/DoNotParallelize.cs b/Console.CustomService/.tests/Tests.Console.CustomService/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.CustomService/.tests/Tests.Console.CustomService/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.CustomService/.tests/Tests.Console.CustomService/Tests.Console.CustomService.csproj b/Console.CustomService/.tests/Tests.Console.CustomService/Tests.Console.CustomService.csproj deleted file mode 100644 index 5912cf8a..00000000 --- a/Console.CustomService/.tests/Tests.Console.CustomService/Tests.Console.CustomService.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.CustomService/Console.CustomService.sln b/Console.CustomService/Console.CustomService.sln deleted file mode 100644 index b4bb6e17..00000000 --- a/Console.CustomService/Console.CustomService.sln +++ /dev/null @@ -1,123 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 d18.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.CustomService", "Console.CustomService\Console.CustomService.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.CustomService", ".tests\Tests.Console.CustomService\Tests.Console.CustomService.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.CustomService/Console.CustomService/Console.CustomService.csproj b/Console.CustomService/Console.CustomService/Console.CustomService.csproj deleted file mode 100644 index e0dfc0c1..00000000 --- a/Console.CustomService/Console.CustomService/Console.CustomService.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Dockerfile.Local b/Console.CustomService/Console.CustomService/Dockerfile.Local deleted file mode 100644 index 0e1d9813..00000000 --- a/Console.CustomService/Console.CustomService/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.CustomService.dll"] \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Program.cs b/Console.CustomService/Console.CustomService/Program.cs deleted file mode 100644 index ba27d399..00000000 --- a/Console.CustomService/Console.CustomService/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Console.CustomService.Services; -using Console.CustomService.Services.Abstractions; -using Microsoft.Extensions.DependencyInjection; -using Nano.App.Console; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddScoped(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Properties/InternalsVisibleTo.cs b/Console.CustomService/Console.CustomService/Properties/InternalsVisibleTo.cs deleted file mode 100644 index ac8322c5..00000000 --- a/Console.CustomService/Console.CustomService/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.CustomService")] \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Services/Abstractions/IExampleService.cs b/Console.CustomService/Console.CustomService/Services/Abstractions/IExampleService.cs deleted file mode 100644 index cf1fd568..00000000 --- a/Console.CustomService/Console.CustomService/Services/Abstractions/IExampleService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Threading.Tasks; - -namespace Console.CustomService.Services.Abstractions; - -/// -/// Example Service Interface. -/// -public interface IExampleService -{ - /// - /// Get Message. - /// - /// A message. - Task GetMessage(); -} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Services/ExampleService.cs b/Console.CustomService/Console.CustomService/Services/ExampleService.cs deleted file mode 100644 index e1c81720..00000000 --- a/Console.CustomService/Console.CustomService/Services/ExampleService.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Threading.Tasks; -using Console.CustomService.Services.Abstractions; - -namespace Console.CustomService.Services; - -/// -/// Example Service. -/// -public class ExampleService : IExampleService -{ - /// - public Task GetMessage() - { - return Task.FromResult("Message from example service."); - } -} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Workers/ExampleWorker.cs b/Console.CustomService/Console.CustomService/Workers/ExampleWorker.cs deleted file mode 100644 index f601ad46..00000000 --- a/Console.CustomService/Console.CustomService/Workers/ExampleWorker.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Console.CustomService.Services.Abstractions; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; - -namespace Console.CustomService.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IExampleService exampleService) : BaseWorker(logger) -{ - private readonly IExampleService exampleService = exampleService ?? throw new ArgumentNullException(nameof(exampleService)); - - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - var message = await this.exampleService - .GetMessage(); - - System.Console.WriteLine(message); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/appsettings.Development.json b/Console.CustomService/Console.CustomService/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.CustomService/Console.CustomService/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/appsettings.Production.json b/Console.CustomService/Console.CustomService/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.CustomService/Console.CustomService/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/appsettings.Staging.json b/Console.CustomService/Console.CustomService/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.CustomService/Console.CustomService/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/appsettings.json b/Console.CustomService/Console.CustomService/appsettings.json deleted file mode 100644 index c7b251f2..00000000 --- a/Console.CustomService/Console.CustomService/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - } -} \ No newline at end of file diff --git a/Console.CustomService/Dockerfile b/Console.CustomService/Dockerfile deleted file mode 100644 index 0486f4ec..00000000 --- a/Console.CustomService/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.CustomService.dll"] \ No newline at end of file diff --git a/Console.CustomService/README.md b/Console.CustomService/README.md deleted file mode 100644 index c1d463da..00000000 --- a/Console.CustomService/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Console.CustomService - -> _Nano Console application with custom scoped service._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates a worker that use a custom service. - -Run the application to see services and hosted services being resolved, and observe the console output generated by the `ExampleWorker` -and the injected `IExampleService` implementation. - -> 📖 Learn more about **[Nano Custom Services](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App#custom-services)**. - -## Registration -A custom service, `IExampleService` has been added and implemented. In `program.cs` the service is registered using `ConfigureService(...)` method as shown below - -```csharp -... -.ConfigureServices(services => -{ - services - .AddScoped(); -}) -... -``` diff --git a/Console.Data.InMemory/.docker/docker-compose.dcproj b/Console.Data.InMemory/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Data.InMemory/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Data.InMemory/.docker/docker-compose.yml b/Console.Data.InMemory/.docker/docker-compose.yml deleted file mode 100644 index e72d1145..00000000 --- a/Console.Data.InMemory/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.data.inmemory: - image: console.data.inmemory - restart: on-failure - build: - context: ../Console.Data.InMemory - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.Data.InMemory/.dockerignore b/Console.Data.InMemory/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Data.InMemory/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Data.InMemory/.github/config/slack.yml b/Console.Data.InMemory/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Data.InMemory/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.InMemory/.github/workflows/build-and-deploy.yml b/Console.Data.InMemory/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 9c77d12a..00000000 --- a/Console.Data.InMemory/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Data.InMemory - IMAGE_NAME: console.data.inmemory - SERVICE_NAME: console-data-inmemory - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.InMemory/.gitignore b/Console.Data.InMemory/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Data.InMemory/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.InMemory/.kubernetes/configmap.yaml b/Console.Data.InMemory/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Data.InMemory/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.InMemory/.kubernetes/cronjob.yaml b/Console.Data.InMemory/.kubernetes/cronjob.yaml deleted file mode 100644 index 1590c0e5..00000000 --- a/Console.Data.InMemory/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Properties/DoNotParallelize.cs b/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Tests.Console.Data.InMemory.csproj b/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Tests.Console.Data.InMemory.csproj deleted file mode 100644 index f7243f3c..00000000 --- a/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Tests.Console.Data.InMemory.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Data.InMemory/Console.Data.InMemory.sln b/Console.Data.InMemory/Console.Data.InMemory.sln deleted file mode 100644 index 30427fbb..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.InMemory", "Console.Data.InMemory\Console.Data.InMemory.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.InMemory", ".tests\Tests.Console.Data.InMemory\Tests.Console.Data.InMemory.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.InMemory", "..\..\Nano.Library\Nano.Data.InMemory\Nano.Data.InMemory.csproj", "{C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Data.InMemory/Console.Data.InMemory/Console.Data.InMemory.csproj b/Console.Data.InMemory/Console.Data.InMemory/Console.Data.InMemory.csproj deleted file mode 100644 index d4f9f974..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/Console.Data.InMemory.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Data/InMemoryDbContext.cs b/Console.Data.InMemory/Console.Data.InMemory/Data/InMemoryDbContext.cs deleted file mode 100644 index 4f1337b4..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/Data/InMemoryDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Console.Data.InMemory.Data; - -/// -public class InMemoryDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Data/Mappings/ExampleMapping.cs b/Console.Data.InMemory/Console.Data.InMemory/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index ef3fdeac..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Console.Data.InMemory.Data.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Console.Data.InMemory.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Data/Models/Example.cs b/Console.Data.InMemory/Console.Data.InMemory/Data/Models/Example.cs deleted file mode 100644 index 2d5ae3a7..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/Data/Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Console.Data.InMemory.Data.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Dockerfile.Local b/Console.Data.InMemory/Console.Data.InMemory/Dockerfile.Local deleted file mode 100644 index a1eafcb3..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Data.InMemory.dll"] \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Program.cs b/Console.Data.InMemory/Console.Data.InMemory/Program.cs deleted file mode 100644 index 14cf320d..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Console.Data.InMemory.Data; -using Nano.App.Console; -using Nano.Data.Extensions; -using Nano.Data.InMemory; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Properties/InternalsVisibleTo.cs b/Console.Data.InMemory/Console.Data.InMemory/Properties/InternalsVisibleTo.cs deleted file mode 100644 index dd0d85af..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Data.InMemory")] \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Workers/ExampleWorker.cs b/Console.Data.InMemory/Console.Data.InMemory/Workers/ExampleWorker.cs deleted file mode 100644 index a495b5a2..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/Workers/ExampleWorker.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Console.Data.InMemory.Data.Models; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using Nano.Data.Abstractions; - -namespace Console.Data.InMemory.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) -{ - private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); - - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - var example = await this.repository - .AddAsync(new Example - { - Name = "name" - }, cancellationToken); - - await this.repository - .SaveChangesAsync(cancellationToken); - - example = await this.repository - .GetAsync(example.Id, cancellationToken); - - System.Console.WriteLine($"The example name is: '{example!.Name}'"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Development.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Production.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Staging.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.json deleted file mode 100644 index 254689c8..00000000 --- a/Console.Data.InMemory/Console.Data.InMemory/appsettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": "nanoDb", - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null - } -} \ No newline at end of file diff --git a/Console.Data.InMemory/Dockerfile b/Console.Data.InMemory/Dockerfile deleted file mode 100644 index 9a0ba936..00000000 --- a/Console.Data.InMemory/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Data.InMemory.dll"] \ No newline at end of file diff --git a/Console.Data.InMemory/README.md b/Console.Data.InMemory/README.md deleted file mode 100644 index 2be04ea4..00000000 --- a/Console.Data.InMemory/README.md +++ /dev/null @@ -1,71 +0,0 @@ -# Console.Data.InMemory - -> _Nano API application with in-memory data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). - -Notice, that the `InMemoryProvider` doesn't require any implementation of `BaseDbContextFactory` and there is no need to add migrations. - -The worker creates and retreives a `Example` entity using the Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories). Run the -application and observe the console output generated by the `ExampleWorker`. - -> 📖 Learn more about **[Nano.Data.InMemory](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.InMemory)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": "nanoDb", - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null -} -``` diff --git a/Console.Data.MySql/.docker/docker-compose.dcproj b/Console.Data.MySql/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Data.MySql/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Data.MySql/.docker/docker-compose.yml b/Console.Data.MySql/.docker/docker-compose.yml deleted file mode 100644 index 3a849720..00000000 --- a/Console.Data.MySql/.docker/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -services: - console.data.mysql: - image: console.data.mysql - restart: on-failure - build: - context: ../Console.Data.MySql - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' - -networks: - network: - name: network - external: true diff --git a/Console.Data.MySql/.dockerignore b/Console.Data.MySql/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Data.MySql/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Data.MySql/.github/config/slack.yml b/Console.Data.MySql/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Data.MySql/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.MySql/.github/workflows/build-and-deploy.yml b/Console.Data.MySql/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 9fc579e7..00000000 --- a/Console.Data.MySql/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,182 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Data.MySql - IMAGE_NAME: console.data.mysql - SERVICE_NAME: console-data-mysql - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING; - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.MySql/.gitignore b/Console.Data.MySql/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Data.MySql/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.MySql/.kubernetes/configmap.yaml b/Console.Data.MySql/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Data.MySql/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.MySql/.kubernetes/cronjob.yaml b/Console.Data.MySql/.kubernetes/cronjob.yaml deleted file mode 100644 index 64e7f060..00000000 --- a/Console.Data.MySql/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Properties/DoNotParallelize.cs b/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Tests.Console.Data.MySql.csproj b/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Tests.Console.Data.MySql.csproj deleted file mode 100644 index fadd2d23..00000000 --- a/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Tests.Console.Data.MySql.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Data.MySql/Console.Data.MySql.sln b/Console.Data.MySql/Console.Data.MySql.sln deleted file mode 100644 index df93c078..00000000 --- a/Console.Data.MySql/Console.Data.MySql.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.MySql", "Console.Data.MySql\Console.Data.MySql.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.MySql", ".tests\Tests.Console.Data.MySql\Tests.Console.Data.MySql.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Data.MySql/Console.Data.MySql/Console.Data.MySql.csproj b/Console.Data.MySql/Console.Data.MySql/Console.Data.MySql.csproj deleted file mode 100644 index 98fd80f3..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Console.Data.MySql.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Data/Mappings/ExampleMapping.cs b/Console.Data.MySql/Console.Data.MySql/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 0b8d424b..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Console.Data.MySql.Data.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Console.Data.MySql.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Data/Models/Example.cs b/Console.Data.MySql/Console.Data.MySql/Data/Models/Example.cs deleted file mode 100644 index 28ea9e14..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Data/Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Console.Data.MySql.Data.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContext.cs b/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContext.cs deleted file mode 100644 index 1aede2a6..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Console.Data.MySql.Data; - -/// -public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContextFactory.cs b/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContextFactory.cs deleted file mode 100644 index 7985d145..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.MySql; - -namespace Console.Data.MySql.Data; - -/// -public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Dockerfile.Local b/Console.Data.MySql/Console.Data.MySql/Dockerfile.Local deleted file mode 100644 index 2b8d2330..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Data.MySql.dll"] \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Migrations/20260311110547_Initial.Designer.cs b/Console.Data.MySql/Console.Data.MySql/Migrations/20260311110547_Initial.Designer.cs deleted file mode 100644 index 0308db61..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Migrations/20260311110547_Initial.Designer.cs +++ /dev/null @@ -1,572 +0,0 @@ -// -using System; -using Console.Data.MySql.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Console.Data.MySql.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - [Migration("20260311110547_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Console.Data.MySql.Data.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityTypeName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("State") - .HasColumnType("int"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.HasIndex("State"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Console.Data.MySql/Console.Data.MySql/Migrations/20260311110547_Initial.cs b/Console.Data.MySql/Console.Data.MySql/Migrations/20260311110547_Initial.cs deleted file mode 100644 index 4de0291f..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Migrations/20260311110547_Initial.cs +++ /dev/null @@ -1,531 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Console.Data.MySql.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - State = table.Column(type: "int", nullable: false), - StateName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - FriendlyName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Xml = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), - UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), - PasswordHash = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - SecurityStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ConcurrencyStamp = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumber = table.Column(type: "varchar(255)", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), - TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), - LockoutEnd = table.Column(type: "datetime(6)", nullable: true), - LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - OldValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Hash = table.Column(type: "longtext", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - RevokedAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - ClaimType = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - ClaimValue = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderKey = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ProviderDisplayName = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4"), - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - ExpireAt = table.Column(type: "datetime(6)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), - LoginProvider = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Name = table.Column(type: "varchar(255)", nullable: false) - .Annotation("MySql:CharSet", "utf8mb4"), - Value = table.Column(type: "longtext", nullable: true) - .Annotation("MySql:CharSet", "utf8mb4") - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySql:CharSet", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_State", - table: "__EFAudit", - column: "State"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId", - table: "__EFIdentityUserRefreshToken", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Console.Data.MySql/Console.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs b/Console.Data.MySql/Console.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs deleted file mode 100644 index a4a80151..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,569 +0,0 @@ -// -using System; -using Console.Data.MySql.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Console.Data.MySql.Migrations -{ - [DbContext(typeof(MySqlDbContext))] - partial class MySqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 64); - - MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); - - modelBuilder.Entity("Console.Data.MySql.Data.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetime(6)"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("varchar(255)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("longtext"); - - b.Property("Xml") - .HasColumnType("longtext"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("longtext"); - - b.Property("ClaimValue") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("ProviderKey") - .HasColumnType("varchar(255)"); - - b.Property("ProviderDisplayName") - .HasColumnType("longtext"); - - b.Property("UserId") - .HasColumnType("char(36)"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("RoleId") - .HasColumnType("char(36)"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("char(36)"); - - b.Property("LoginProvider") - .HasColumnType("varchar(255)"); - - b.Property("Name") - .HasColumnType("varchar(255)"); - - b.Property("Value") - .HasColumnType("longtext"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("CreatedBy") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EntityTypeName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("State") - .HasColumnType("int"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.HasIndex("State"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("longtext"); - - b.Property("OldValue") - .HasColumnType("longtext"); - - b.Property("ParentId") - .HasColumnType("char(36)"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("CreatedAt") - .HasColumnType("datetime(6)"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("longtext"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetime(6)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("varchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("longtext"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("tinyint(1)") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("LockoutEnd") - .HasColumnType("datetime(6)"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("longtext"); - - b.Property("PhoneNumber") - .HasColumnType("varchar(255)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); - - b.Property("SecurityStamp") - .HasColumnType("longtext"); - - b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetime(6)"); - - b.Property("IdentityUserId") - .HasColumnType("char(36)"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("varchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Console.Data.MySql/Console.Data.MySql/Program.cs b/Console.Data.MySql/Console.Data.MySql/Program.cs deleted file mode 100644 index b81a098d..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Console.Data.MySql.Data; -using Nano.App.Console; -using Nano.Data.Extensions; -using Nano.Data.MySql; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Properties/InternalsVisibleTo.cs b/Console.Data.MySql/Console.Data.MySql/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 132b71d7..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Data.MySql")] \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Workers/ExampleWorker.cs b/Console.Data.MySql/Console.Data.MySql/Workers/ExampleWorker.cs deleted file mode 100644 index dda41519..00000000 --- a/Console.Data.MySql/Console.Data.MySql/Workers/ExampleWorker.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Console.Data.MySql.Data.Models; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using Nano.Data.Abstractions; - -namespace Console.Data.MySql.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) -{ - private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); - - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - var example = await this.repository - .AddAsync(new Example - { - Name = "name" - }, cancellationToken); - - await this.repository - .SaveChangesAsync(cancellationToken); - - example = await this.repository - .GetAsync(example.Id, cancellationToken); - - System.Console.WriteLine($"The example name is: '{example!.Name}'"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json b/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json deleted file mode 100644 index e586906a..00000000 --- a/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "UseMigrateDatabase": true, - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" - } -} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.Production.json b/Console.Data.MySql/Console.Data.MySql/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.MySql/Console.Data.MySql/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.Staging.json b/Console.Data.MySql/Console.Data.MySql/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.MySql/Console.Data.MySql/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.json b/Console.Data.MySql/Console.Data.MySql/appsettings.json deleted file mode 100644 index f52b7fe7..00000000 --- a/Console.Data.MySql/Console.Data.MySql/appsettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null - } -} \ No newline at end of file diff --git a/Console.Data.MySql/Dockerfile b/Console.Data.MySql/Dockerfile deleted file mode 100644 index db692014..00000000 --- a/Console.Data.MySql/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Data.MySql.dll"] \ No newline at end of file diff --git a/Console.Data.MySql/README.md b/Console.Data.MySql/README.md deleted file mode 100644 index 80016f00..00000000 --- a/Console.Data.MySql/README.md +++ /dev/null @@ -1,183 +0,0 @@ -# Console.Data.MySql - -> _Nano API application with mysql data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). - -The worker creates and retreives a `Example` entity using the Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories). Run the -application and observe the console output generated by the `ExampleWorker`. - -> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -Also, an initial migration has been added to the project. - -```powershell -dotnet ef migrations add Initial --project Console.Data.MySql -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null -} -``` - -...and `appsettings.Development.json` - -```json -"Data": { - "UseMigrateDatabase": true, - "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" -} -``` - -## Docker Compose -Added MySql as a service dependency in `docker-compose.yml`. - -```yaml -services: - console.data.mysql: - depends_on: - - database - - database: - image: mysql/mysql-server:latest - ports: - - 3306:3306 - networks: - - network - environment: - MYSQL_USER: sa - MYSQL_PASSWORD: myPassword_123 - MYSQL_ROOT_PASSWORD: myPassword_123 - MYSQL_DATABASE: nanoDb - MYSQL_ROOT_HOST: '%' -``` - -## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. - -```json -spec: - jobTemplate: - spec: - template: - spec: - containers: - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_HOST || secrets.STAGING_MYSQL_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-mysql-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_NANO_DB_PASSWORD || secrets.STAGING_MYSQL_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_USER || secrets.STAGING_MYSQL_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_MYSQL_ADMIN_PASSWORD || secrets.STAGING_MYSQL_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_USER }};Pwd=${{ env.DATA_PASSWORD }};SslMode=Preferred; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }};Port=${{ vars.DATA_MYSQL_PORT }};Database=${{ env.DATA_NAME }};Uid=${{ env.DATA_ADMIN_USER }};Pwd=${{ env.DATA_ADMIN_PASSWORD }};SslMode=Preferred; -``` - -Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mysql-client - - $userExists = mysql --connect-expired-password --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:DATA_USER');" $env:DATA_MIGRATION_CONNECTIONSTRING; - - if ($userExists -eq 0) - { - mysql --connect-expired-password -e " ` - CREATE USER '$env:DATA_USER'@'%' IDENTIFIED BY '$env:DATA_PASSWORD'; ` - GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:DATA_USER'@'%'; ` - FLUSH PRIVILEGES;" $env:DATA_MIGRATION_CONNECTIONSTRING - } -``` - -Last, the application connectionstring must be added in a secret in Kuberntes. The `Kubernetes Deploy` step has been updated with the following. - -```yaml -sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Console.Data.PostgreSQL/.docker/docker-compose.dcproj b/Console.Data.PostgreSQL/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Data.PostgreSQL/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Data.PostgreSQL/.docker/docker-compose.yml b/Console.Data.PostgreSQL/.docker/docker-compose.yml deleted file mode 100644 index c3f9dac1..00000000 --- a/Console.Data.PostgreSQL/.docker/docker-compose.yml +++ /dev/null @@ -1,27 +0,0 @@ -services: - console.data.postgresql: - image: console.data.postgresql - restart: on-failure - build: - context: ../Console.Data.PostgreSQL - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: postgis/postgis:latest - ports: - - 5432:5432 - networks: - - network - environment: - POSTGRES_USER: sa - POSTGRES_PASSWORD: myPassword_123 - POSTGRES_DB: nanoDb - -networks: - network: - name: network - external: true diff --git a/Console.Data.PostgreSQL/.dockerignore b/Console.Data.PostgreSQL/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Data.PostgreSQL/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Data.PostgreSQL/.github/config/slack.yml b/Console.Data.PostgreSQL/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Data.PostgreSQL/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml b/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml deleted file mode 100644 index deb4c064..00000000 --- a/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,200 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Data.PostgreSQL - IMAGE_NAME: console.data.postgresql - SERVICE_NAME: console-data-postgresql - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_HOST || secrets.STAGING_POSTGRE_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-postgre-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_NANO_DB_PASSWORD || secrets.STAGING_POSTGRE_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_USER || secrets.STAGING_POSTGRE_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_PASSWORD || secrets.STAGING_POSTGRE_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true - DATA_MIGRATION_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y postgresql-client - - $userExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -c "CREATE ROLE $env:DATA_USER WITH LOGIN PASSWORD '$env:DATA_PASSWORD';" - } - - $userDbExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userDbExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT CONNECT ON DATABASE $env:DATA_NAME TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT USAGE ON SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:DATA_USER;" - } - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:MYSQL_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.PostgreSQL/.gitignore b/Console.Data.PostgreSQL/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Data.PostgreSQL/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.PostgreSQL/.kubernetes/configmap.yaml b/Console.Data.PostgreSQL/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Data.PostgreSQL/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml b/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml deleted file mode 100644 index 64e7f060..00000000 --- a/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Properties/DoNotParallelize.cs b/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Tests.Console.Data.PostgreSQL.csproj b/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Tests.Console.Data.PostgreSQL.csproj deleted file mode 100644 index 2be29a09..00000000 --- a/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Tests.Console.Data.PostgreSQL.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL.sln b/Console.Data.PostgreSQL/Console.Data.PostgreSQL.sln deleted file mode 100644 index c286d388..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.PostgreSQL", "Console.Data.PostgreSQL\Console.Data.PostgreSQL.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.PostgreSQL", ".tests\Tests.Console.Data.PostgreSQL\Tests.Console.Data.PostgreSQL.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.PostgreSQL", "..\..\Nano.Library\Nano.Data.PostgreSQL\Nano.Data.PostgreSQL.csproj", "{B898497D-7C77-3353-B323-399EBAF53DAC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {B898497D-7C77-3353-B323-399EBAF53DAC} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Console.Data.PostgreSQL.csproj b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Console.Data.PostgreSQL.csproj deleted file mode 100644 index d449cf84..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Console.Data.PostgreSQL.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index 059abbc5..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Console.Data.PostgreSQL.Data.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Console.Data.PostgreSQL.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Models/Example.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Models/Example.cs deleted file mode 100644 index 7f869bb9..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Console.Data.PostgreSQL.Data.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContext.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContext.cs deleted file mode 100644 index 0266c587..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Console.Data.PostgreSQL.Data; - -/// -public class PostgreSqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs deleted file mode 100644 index 18a049ec..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.PostgreSQL; - -namespace Console.Data.PostgreSQL.Data; - -/// -public class PostgreSqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Dockerfile.Local b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Dockerfile.Local deleted file mode 100644 index 5ebef1e3..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260311120716_Initial.Designer.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260311120716_Initial.Designer.cs deleted file mode 100644 index f219b9b6..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260311120716_Initial.Designer.cs +++ /dev/null @@ -1,571 +0,0 @@ -// -using System; -using Console.Data.PostgreSQL.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Console.Data.PostgreSQL.Migrations -{ - [DbContext(typeof(PostgreSqlDbContext))] - [Migration("20260311120716_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Console.Data.PostgreSQL.Data.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("text"); - - b.Property("Xml") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityTypeName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("State") - .HasColumnType("integer"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.HasIndex("State"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("text"); - - b.Property("OldValue") - .HasColumnType("text"); - - b.Property("ParentId") - .HasColumnType("uuid"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("text"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260311120716_Initial.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260311120716_Initial.cs deleted file mode 100644 index 6411abcb..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260311120716_Initial.cs +++ /dev/null @@ -1,477 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Console.Data.PostgreSQL.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:PostgresExtension:postgis", ",,"); - - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - CreatedBy = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EntitySetName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EntityTypeName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - State = table.Column(type: "integer", nullable: false), - StateName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - RequestId = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - FriendlyName = table.Column(type: "text", nullable: true), - Xml = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IsActive = table.Column(type: "boolean", nullable: false, defaultValue: true), - UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "boolean", nullable: false), - PasswordHash = table.Column(type: "text", nullable: true), - SecurityStamp = table.Column(type: "text", nullable: true), - ConcurrencyStamp = table.Column(type: "text", nullable: true), - PhoneNumber = table.Column(type: "text", nullable: true), - PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), - TwoFactorEnabled = table.Column(type: "boolean", nullable: false), - LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), - LockoutEnabled = table.Column(type: "boolean", nullable: false), - AccessFailedCount = table.Column(type: "integer", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "text", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - ParentId = table.Column(type: "uuid", nullable: false), - PropertyName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - RelationName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NewValue = table.Column(type: "text", nullable: true), - OldValue = table.Column(type: "text", nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - RoleId = table.Column(type: "uuid", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - Hash = table.Column(type: "text", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - RevokedAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - NewEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), - NewPhoneNumber = table.Column(type: "character varying(20)", maxLength: 20, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - UserId = table.Column(type: "uuid", nullable: false), - ClaimType = table.Column(type: "text", nullable: true), - ClaimValue = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "text", nullable: false), - ProviderKey = table.Column(type: "text", nullable: false), - ProviderDisplayName = table.Column(type: "text", nullable: true), - UserId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - IdentityUserId = table.Column(type: "uuid", nullable: false), - AppId = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - Value = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), - ExpireAt = table.Column(type: "timestamp with time zone", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "uuid", nullable: false), - RoleId = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "uuid", nullable: false), - LoginProvider = table.Column(type: "text", nullable: false), - Name = table.Column(type: "text", nullable: false), - Value = table.Column(type: "text", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_State", - table: "__EFAudit", - column: "State"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId", - table: "__EFIdentityUserRefreshToken", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs deleted file mode 100644 index 9e4e9603..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs +++ /dev/null @@ -1,568 +0,0 @@ -// -using System; -using Console.Data.PostgreSQL.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace Console.Data.PostgreSQL.Migrations -{ - [DbContext(typeof(PostgreSqlDbContext))] - partial class PostgreSqlDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("Console.Data.PostgreSQL.Data.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("text"); - - b.Property("Xml") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("uuid"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("RoleId") - .HasColumnType("uuid"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uuid"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("CreatedBy") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EntityTypeName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("State") - .HasColumnType("integer"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.HasIndex("State"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("text"); - - b.Property("OldValue") - .HasColumnType("text"); - - b.Property("ParentId") - .HasColumnType("uuid"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("text"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("RevokedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("boolean") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("ExpireAt") - .HasColumnType("timestamp with time zone"); - - b.Property("IdentityUserId") - .HasColumnType("uuid"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs deleted file mode 100644 index 23a3340f..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Console.Data.PostgreSQL.Data; -using Nano.App.Console; -using Nano.Data.Extensions; -using Nano.Data.PostgreSQL; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Properties/InternalsVisibleTo.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Properties/InternalsVisibleTo.cs deleted file mode 100644 index efb5ad62..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Data.PostgreSQL")] \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Workers/ExampleWorker.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Workers/ExampleWorker.cs deleted file mode 100644 index fee31fe2..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Workers/ExampleWorker.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Console.Data.PostgreSQL.Data.Models; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using Nano.Data.Abstractions; - -namespace Console.Data.PostgreSQL.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) -{ - private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); - - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - var example = await this.repository - .AddAsync(new Example - { - Name = "name" - }, cancellationToken); - - await this.repository - .SaveChangesAsync(cancellationToken); - - example = await this.repository - .GetAsync(example.Id, cancellationToken); - - System.Console.WriteLine($"The example name is: '{example!.Name}'"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Development.json b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Development.json deleted file mode 100644 index abb3ca92..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "UseMigrateDatabase": true, - "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" - } -} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Production.json b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Staging.json b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.json b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.json deleted file mode 100644 index f52b7fe7..00000000 --- a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null - } -} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Dockerfile b/Console.Data.PostgreSQL/Dockerfile deleted file mode 100644 index b9fe9470..00000000 --- a/Console.Data.PostgreSQL/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Console.Data.PostgreSQL/README.md b/Console.Data.PostgreSQL/README.md deleted file mode 100644 index 39dee06a..00000000 --- a/Console.Data.PostgreSQL/README.md +++ /dev/null @@ -1,196 +0,0 @@ -# Console.Data.PostgreSQL - -> _Nano API application with postgresql data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). - -The worker creates and retreives a `Example` entity using the Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories). Run the -application and observe the console output generated by the `ExampleWorker`. - -> 📖 Learn more about **[Nano.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.PostgreSQL)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -Also, an initial migration has been added to the project. - -```powershell -dotnet ef migrations add Initial --project Console.Data.PostgreSQL -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null -} -``` - -...and `appsettings.Development.json` - -```json -"Data": { - "UseMigrateDatabase": true, - "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" -} -``` - -## Docker Compose -Added PostgreSQL as a service dependency in `docker-compose.yml`. - -```yaml -services: - console.data.postgresql: - depends_on: - - database - - database: - image: postgis/postgis:latest - ports: - - 5432:5432 - networks: - - network - environment: - POSTGRES_USER: sa - POSTGRES_PASSWORD: myPassword_123 - POSTGRES_DB: nanoDb -``` - -## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. - -```json -spec: - template: - spec: - containers: - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_HOST || secrets.STAGING_POSTGRE_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-postgre-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_NANO_DB_PASSWORD || secrets.STAGING_POSTGRE_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_USER || secrets.STAGING_POSTGRE_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_POSTGRE_ADMIN_PASSWORD || secrets.STAGING_POSTGRE_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true - DATA_MIGRATION_CONNECTIONSTRING: Host=${{ env.DATA_HOST }};Port=${{ vars.DATA_POSTGRE_PORT }};Database=${{ env.DATA_NAME }};Username=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }};SSL Mode=Prefer;Trust Server Certificate=true -``` - -Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y postgresql-client - - $userExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=postgres" ` - -c "CREATE ROLE $env:DATA_USER WITH LOGIN PASSWORD '$env:DATA_PASSWORD';" - } - - $userDbExists = psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:DATA_USER';" - - if ($userDbExists -ne "1") - { - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT CONNECT ON DATABASE $env:DATA_NAME TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT USAGE ON SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:DATA_USER;" - - psql "host=$env:DATA_HOST port=$env:DATA_POSTGRE_PORT user=$env:DATA_ADMIN_USER password=$env:DATA_ADMIN_PASSWORD dbname=$env:DATA_NAME" ` - -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:DATA_USER;" - } -``` - -Last, the application connectionstring must be added in a secret in Kuberntes. The `Kubernetes Deploy` step has been updated with the following. - -```yaml -sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Console.Data.SqLite/.docker/docker-compose.dcproj b/Console.Data.SqLite/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Data.SqLite/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Data.SqLite/.docker/docker-compose.yml b/Console.Data.SqLite/.docker/docker-compose.yml deleted file mode 100644 index db8bdb53..00000000 --- a/Console.Data.SqLite/.docker/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -services: - console.data.sqlite: - image: console.data.sqlite - restart: on-failure - build: - context: ../Console.Data.SqLite - dockerfile: "Dockerfile.Local" - volumes: - - ./bin/data:/data - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.Data.SqLite/.dockerignore b/Console.Data.SqLite/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Data.SqLite/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Data.SqLite/.github/config/slack.yml b/Console.Data.SqLite/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Data.SqLite/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.SqLite/.github/workflows/build-and-deploy.yml b/Console.Data.SqLite/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 95a69c0d..00000000 --- a/Console.Data.SqLite/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,173 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Data.SqLite - IMAGE_NAME: console.data.sqlite - SERVICE_NAME: console-data-sqlite - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DATA_NAME: nanoDb - DATA_SIZE: 10Gi - DATA_CONNECTIONSTRING: "Data Source=/data/{{ env.nanoDb }}.sqlite" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING"; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/data-storageclass.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/data-storageclass.tmp.yaml; - sudo kubectl apply -f .kubernetes/data-storageclass.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/data-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/data-pvc.tmp.yaml; - sudo kubectl apply -f .kubernetes/data-pvc.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.SqLite/.gitignore b/Console.Data.SqLite/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Data.SqLite/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.SqLite/.kubernetes/configmap.yaml b/Console.Data.SqLite/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Data.SqLite/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.SqLite/.kubernetes/cronjob.yaml b/Console.Data.SqLite/.kubernetes/cronjob.yaml deleted file mode 100644 index 243d9b9c..00000000 --- a/Console.Data.SqLite/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.SqLite/.kubernetes/data-pvc.yaml b/Console.Data.SqLite/.kubernetes/data-pvc.yaml deleted file mode 100644 index 0b9d4415..00000000 --- a/Console.Data.SqLite/.kubernetes/data-pvc.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: %SERVICE_NAME%-data-pvc -spec: - accessModes: - - ReadWriteOnce - storageClassName: %SERVICE_NAME%-data-storage-class - resources: - requests: - storage: %DATA_SIZE% \ No newline at end of file diff --git a/Console.Data.SqLite/.kubernetes/data-storageclass.yaml b/Console.Data.SqLite/.kubernetes/data-storageclass.yaml deleted file mode 100644 index 47530cdc..00000000 --- a/Console.Data.SqLite/.kubernetes/data-storageclass.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: %SERVICE_NAME%-data-storage-class -provisioner: kubernetes.io/azure-disk -parameters: - storageaccounttype: Standard_LRS - kind: Managed -reclaimPolicy: Retain -volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Properties/DoNotParallelize.cs b/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Tests.Console.Data.SqLite.csproj b/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Tests.Console.Data.SqLite.csproj deleted file mode 100644 index b5e793e0..00000000 --- a/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Tests.Console.Data.SqLite.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Data.SqLite/Console.Data.SqLite.sln b/Console.Data.SqLite/Console.Data.SqLite.sln deleted file mode 100644 index 365797b7..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite.sln +++ /dev/null @@ -1,139 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.SqLite", "Console.Data.SqLite\Console.Data.SqLite.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - .kubernetes\data-pvc.yaml = .kubernetes\data-pvc.yaml - .kubernetes\data-storageclass.yaml = .kubernetes\data-storageclass.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.SqLite", ".tests\Tests.Console.Data.SqLite\Tests.Console.Data.SqLite.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqLite", "..\..\Nano.Library\Nano.Data.SqLite\Nano.Data.SqLite.csproj", "{0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Data.SqLite/Console.Data.SqLite/Console.Data.SqLite.csproj b/Console.Data.SqLite/Console.Data.SqLite/Console.Data.SqLite.csproj deleted file mode 100644 index 43996cd4..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Console.Data.SqLite.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Data/Mappings/ExampleMapping.cs b/Console.Data.SqLite/Console.Data.SqLite/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index bbbf1fab..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Console.Data.SqLite.Data.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Console.Data.SqLite.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Data/Models/Example.cs b/Console.Data.SqLite/Console.Data.SqLite/Data/Models/Example.cs deleted file mode 100644 index ba4c0efc..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Data/Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Console.Data.SqLite.Data.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContext.cs b/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContext.cs deleted file mode 100644 index 843fde90..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Console.Data.SqLite.Data; - -/// -public class SqLiteDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContextFactory.cs b/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContextFactory.cs deleted file mode 100644 index f202fe8a..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.SqLite; - -namespace Console.Data.SqLite.Data; - -/// -public class SqLiteDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Dockerfile.Local b/Console.Data.SqLite/Console.Data.SqLite/Dockerfile.Local deleted file mode 100644 index c3dd41cd..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Dockerfile.Local +++ /dev/null @@ -1,9 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -RUN apt-get update \ - && apt-get install -y libsqlite3-mod-spatialite \ - && apt-get install -y libspatialite-dev - -ENTRYPOINT ["dotnet", "Console.Data.SqLite.dll"] \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260311131050_Initial.Designer.cs b/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260311131050_Initial.Designer.cs deleted file mode 100644 index e9c26d2f..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260311131050_Initial.Designer.cs +++ /dev/null @@ -1,559 +0,0 @@ -// -using System; -using Console.Data.SqLite.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Console.Data.SqLite.Migrations -{ - [DbContext(typeof(SqLiteDbContext))] - [Migration("20260311131050_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.11"); - - modelBuilder.Entity("Console.Data.SqLite.Data.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("FriendlyName") - .HasColumnType("TEXT"); - - b.Property("Xml") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("ProviderKey") - .HasColumnType("TEXT"); - - b.Property("ProviderDisplayName") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EntityTypeName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .HasColumnType("INTEGER"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("State") - .HasColumnType("INTEGER"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.HasIndex("State"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .HasColumnType("INTEGER"); - - b.Property("NewValue") - .HasColumnType("TEXT"); - - b.Property("OldValue") - .HasColumnType("TEXT"); - - b.Property("ParentId") - .HasColumnType("TEXT"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("RevokedAt") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AccessFailedCount") - .HasColumnType("INTEGER"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EmailConfirmed") - .HasColumnType("INTEGER"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("INTEGER"); - - b.Property("LockoutEnd") - .HasColumnType("TEXT"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PhoneNumber") - .HasColumnType("TEXT"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("INTEGER"); - - b.Property("SecurityStamp") - .HasColumnType("TEXT"); - - b.Property("TwoFactorEnabled") - .HasColumnType("INTEGER"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("ExpireAt") - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260311131050_Initial.cs b/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260311131050_Initial.cs deleted file mode 100644 index 3cc2b508..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260311131050_Initial.cs +++ /dev/null @@ -1,473 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Console.Data.SqLite.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - CreatedBy = table.Column(type: "TEXT", maxLength: 256, nullable: true), - EntitySetName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - EntityTypeName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - State = table.Column(type: "INTEGER", nullable: false), - StateName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - RequestId = table.Column(type: "TEXT", maxLength: 256, nullable: true), - IsDeleted = table.Column(type: "INTEGER", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - FriendlyName = table.Column(type: "TEXT", nullable: true), - Xml = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - IsActive = table.Column(type: "INTEGER", nullable: false, defaultValue: true), - UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "INTEGER", nullable: false), - PasswordHash = table.Column(type: "TEXT", nullable: true), - SecurityStamp = table.Column(type: "TEXT", nullable: true), - ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), - PhoneNumber = table.Column(type: "TEXT", nullable: true), - PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), - TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), - LockoutEnd = table.Column(type: "TEXT", nullable: true), - LockoutEnabled = table.Column(type: "INTEGER", nullable: false), - AccessFailedCount = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - ParentId = table.Column(type: "TEXT", nullable: false), - PropertyName = table.Column(type: "TEXT", maxLength: 256, nullable: false), - RelationName = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NewValue = table.Column(type: "TEXT", nullable: true), - OldValue = table.Column(type: "TEXT", nullable: true), - IsDeleted = table.Column(type: "INTEGER", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - RoleId = table.Column(type: "TEXT", nullable: false), - ClaimType = table.Column(type: "TEXT", nullable: true), - ClaimValue = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - IdentityUserId = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", maxLength: 256, nullable: false), - Hash = table.Column(type: "TEXT", nullable: false), - CreatedAt = table.Column(type: "TEXT", nullable: false), - RevokedAt = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - IdentityUserId = table.Column(type: "TEXT", nullable: false), - NewEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), - NewPhoneNumber = table.Column(type: "TEXT", maxLength: 20, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - UserId = table.Column(type: "TEXT", nullable: false), - ClaimType = table.Column(type: "TEXT", nullable: true), - ClaimValue = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "TEXT", nullable: false), - ProviderKey = table.Column(type: "TEXT", nullable: false), - ProviderDisplayName = table.Column(type: "TEXT", nullable: true), - UserId = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "TEXT", nullable: false), - IdentityUserId = table.Column(type: "TEXT", nullable: false), - AppId = table.Column(type: "TEXT", maxLength: 256, nullable: false), - Value = table.Column(type: "TEXT", maxLength: 256, nullable: false), - ExpireAt = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "TEXT", nullable: false), - RoleId = table.Column(type: "TEXT", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "TEXT", nullable: false), - LoginProvider = table.Column(type: "TEXT", nullable: false), - Name = table.Column(type: "TEXT", nullable: false), - Value = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_State", - table: "__EFAudit", - column: "State"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId", - table: "__EFIdentityUserRefreshToken", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Console.Data.SqLite/Console.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs b/Console.Data.SqLite/Console.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs deleted file mode 100644 index 3726e496..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs +++ /dev/null @@ -1,556 +0,0 @@ -// -using System; -using Console.Data.SqLite.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Console.Data.SqLite.Migrations -{ - [DbContext(typeof(SqLiteDbContext))] - partial class SqLiteDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "9.0.11"); - - modelBuilder.Entity("Console.Data.SqLite.Data.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("FriendlyName") - .HasColumnType("TEXT"); - - b.Property("Xml") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("ClaimType") - .HasColumnType("TEXT"); - - b.Property("ClaimValue") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("ProviderKey") - .HasColumnType("TEXT"); - - b.Property("ProviderDisplayName") - .HasColumnType("TEXT"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("RoleId") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("TEXT"); - - b.Property("LoginProvider") - .HasColumnType("TEXT"); - - b.Property("Name") - .HasColumnType("TEXT"); - - b.Property("Value") - .HasColumnType("TEXT"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("CreatedBy") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EntityTypeName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .HasColumnType("INTEGER"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("State") - .HasColumnType("INTEGER"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.HasIndex("State"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("IsDeleted") - .HasColumnType("INTEGER"); - - b.Property("NewValue") - .HasColumnType("TEXT"); - - b.Property("OldValue") - .HasColumnType("TEXT"); - - b.Property("ParentId") - .HasColumnType("TEXT"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("CreatedAt") - .HasColumnType("TEXT"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("RevokedAt") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AccessFailedCount") - .HasColumnType("INTEGER"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("TEXT"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("EmailConfirmed") - .HasColumnType("INTEGER"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("INTEGER"); - - b.Property("LockoutEnd") - .HasColumnType("TEXT"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("PasswordHash") - .HasColumnType("TEXT"); - - b.Property("PhoneNumber") - .HasColumnType("TEXT"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("INTEGER"); - - b.Property("SecurityStamp") - .HasColumnType("TEXT"); - - b.Property("TwoFactorEnabled") - .HasColumnType("INTEGER"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique(); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PhoneNumber") - .IsUnique(); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.Property("ExpireAt") - .HasColumnType("TEXT"); - - b.Property("IdentityUserId") - .HasColumnType("TEXT"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Console.Data.SqLite/Console.Data.SqLite/Program.cs b/Console.Data.SqLite/Console.Data.SqLite/Program.cs deleted file mode 100644 index cac43631..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Console.Data.SqLite.Data; -using Nano.App.Console; -using Nano.Data.Extensions; -using Nano.Data.SqLite; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Properties/InternalsVisibleTo.cs b/Console.Data.SqLite/Console.Data.SqLite/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 3b8565f7..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Data.SqLite")] \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Workers/ExampleWorker.cs b/Console.Data.SqLite/Console.Data.SqLite/Workers/ExampleWorker.cs deleted file mode 100644 index eb84169a..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/Workers/ExampleWorker.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Console.Data.SqLite.Data.Models; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using Nano.Data.Abstractions; - -namespace Console.Data.SqLite.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) -{ - private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); - - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - var example = await this.repository - .AddAsync(new Example - { - Name = "name" - }, cancellationToken); - - await this.repository - .SaveChangesAsync(cancellationToken); - - example = await this.repository - .GetAsync(example.Id, cancellationToken); - - System.Console.WriteLine($"The example name is: '{example!.Name}'"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json deleted file mode 100644 index 434b55cf..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Data": { - "UseMigrateDatabase": true - } -} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Production.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Staging.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.json deleted file mode 100644 index 2f4bba6c..00000000 --- a/Console.Data.SqLite/Console.Data.SqLite/appsettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": "Data Source=/data/nanoDb.sqlite", - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "ConnectionPool": null, - "Identity": null, - "HealthCheck": null - } -} \ No newline at end of file diff --git a/Console.Data.SqLite/Dockerfile b/Console.Data.SqLite/Dockerfile deleted file mode 100644 index f6e105a6..00000000 --- a/Console.Data.SqLite/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -RUN apt-get update \ - && apt-get install -y libsqlite3-mod-spatialite \ - && apt-get install -y libspatialite-dev - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Data.SqLite.dll"] \ No newline at end of file diff --git a/Console.Data.SqLite/README.md b/Console.Data.SqLite/README.md deleted file mode 100644 index 029ddf3e..00000000 --- a/Console.Data.SqLite/README.md +++ /dev/null @@ -1,162 +0,0 @@ -# Console.Data.SqLite - -> _Nano API application with sqlite data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). - -The worker creates and retreives a `Example` entity using the Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories). Run the -application and observe the console output generated by the `ExampleWorker`. - -> 📖 Learn more about **[Nano.Data.SqLite](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqLite)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -Also, an initial migration has been added to the project. - -```powershell -dotnet ef migrations add Initial --project Console.Data.SqLite -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": "Data Source=/data/nanoDb.sqlite", - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null -} -``` - -...and `appsettings.Development.json` - -```json -"Data": { - "UseMigrateDatabase": true -} -``` - -## Docker Compose -Added SqLite as a service dependency in `docker-compose.yml`. - -```yaml -services: - console.data.sqlite: - volumes: - - ./bin/data:/data -``` - -Also the `Dockerfile` must have SqLite installed with spatial support. Add the following to both the `Dockerfile` and the `Dockerfile.Local`. - -```dockerfile -RUN apt-get update \ - && apt-get install -y libsqlite3-mod-spatialite \ - && apt-get install -y libspatialite-dev -``` - -## Kubernetes -Added two additional kubernetes templates, `storageclass.yaml` and `pvc.yaml`, for dynamically manage and creating the disk for the SqLite database. - -Also, updated `cronjob.yaml` adding the volumes and volume mounts. - -```json -spec: - jobTemplate: - spec: - template: - spec: - containers: - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - DATA_NAME: nanoDb - DATA_SIZE: 10Gi - DATA_CONNECTIONSTRING: "Data Source=/data/{{ env.nanoDb }}.sqlite" -``` - -Additionally, this step has been added to ensure database migrations are applied. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; -``` - -Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Console.Data.SqlServer/.docker/docker-compose.dcproj b/Console.Data.SqlServer/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Data.SqlServer/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Data.SqlServer/.docker/docker-compose.yml b/Console.Data.SqlServer/.docker/docker-compose.yml deleted file mode 100644 index 423dc083..00000000 --- a/Console.Data.SqlServer/.docker/docker-compose.yml +++ /dev/null @@ -1,27 +0,0 @@ -services: - console.data.sqlserver: - image: console.data.sqlserver - restart: on-failure - build: - context: ../Console.Data.SqlServer - dockerfile: "Dockerfile.Local" - depends_on: - - database - networks: - - network - - database: - image: mcr.microsoft.com/mssql/server:2022-latest - ports: - - 1433:1433 - networks: - - network - environment: - SA_PASSWORD: myPassword_123 - ACCEPT_EULA: Y - MSSQL_PID: Developer - -networks: - network: - name: network - external: true diff --git a/Console.Data.SqlServer/.dockerignore b/Console.Data.SqlServer/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Data.SqlServer/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Data.SqlServer/.github/config/slack.yml b/Console.Data.SqlServer/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Data.SqlServer/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml b/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml deleted file mode 100644 index dd464657..00000000 --- a/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,212 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Data.SqlServer - IMAGE_NAME: console.data.sqlserver - SERVICE_NAME: console-data-sqlserver - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_HOST || secrets.STAGING_SQLSERVER_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-sqlserver-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_NANO_DB_PASSWORD || secrets.STAGING_SQLSERVER_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_USER || secrets.STAGING_SQLSERVER_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_PASSWORD || secrets.STAGING_SQLSERVER_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }}; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }}; - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - - name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mssql-tools unixodbc-dev - - $loginExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:DATA_USER';" - - if ($loginExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -Q "CREATE LOGIN [$env:DATA_USER] WITH PASSWORD = '$env:DATA_PASSWORD';" - }; - - $userExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:DATA_USER';" - - if ($userExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -Q "CREATE USER [$env:DATA_USER] FOR LOGIN [$env:DATA_USER]; - ALTER ROLE db_datareader ADD MEMBER [$env:DATA_USER]; - ALTER ROLE db_datawriter ADD MEMBER [$env:DATA_USER];" - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.SqlServer/.gitignore b/Console.Data.SqlServer/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Data.SqlServer/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.SqlServer/.kubernetes/configmap.yaml b/Console.Data.SqlServer/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Data.SqlServer/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.SqlServer/.kubernetes/cronjob.yaml b/Console.Data.SqlServer/.kubernetes/cronjob.yaml deleted file mode 100644 index 64e7f060..00000000 --- a/Console.Data.SqlServer/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-data-secret - key: data-connectionstring - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Properties/DoNotParallelize.cs b/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Tests.Console.Data.SqlServer.csproj b/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Tests.Console.Data.SqlServer.csproj deleted file mode 100644 index c97cb86e..00000000 --- a/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Tests.Console.Data.SqlServer.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Data.SqlServer/Console.Data.SqlServer.sln b/Console.Data.SqlServer/Console.Data.SqlServer.sln deleted file mode 100644 index b9fbd851..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.SqlServer", "Console.Data.SqlServer\Console.Data.SqlServer.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.SqlServer", ".tests\Tests.Console.Data.SqlServer\Tests.Console.Data.SqlServer.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqlServer", "..\..\Nano.Library\Nano.Data.SqlServer\Nano.Data.SqlServer.csproj", "{FE8B04D5-8AED-F023-588B-65947B83FDF5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.Build.0 = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {FE8B04D5-8AED-F023-588B-65947B83FDF5} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Console.Data.SqlServer.csproj b/Console.Data.SqlServer/Console.Data.SqlServer/Console.Data.SqlServer.csproj deleted file mode 100644 index 90a4a93b..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Console.Data.SqlServer.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Data/Mappings/ExampleMapping.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Data/Mappings/ExampleMapping.cs deleted file mode 100644 index dba812d8..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Data/Mappings/ExampleMapping.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Console.Data.SqlServer.Data.Models; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Nano.Data.Mappings; - -namespace Console.Data.SqlServer.Data.Mappings; - -/// -/// Example Mapping. -/// -public class ExampleMapping : BaseEntityMapping -{ - /// - public override void Configure(EntityTypeBuilder builder) - { - ArgumentNullException.ThrowIfNull(builder); - - base.Configure(builder); - - builder - .Property(x => x.Name); - - builder - .HasIndex(x => x.Name); - } -} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Data/Models/Example.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Data/Models/Example.cs deleted file mode 100644 index d60dc3f2..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Data/Models/Example.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Nano.Data.Abstractions.Models; - -namespace Console.Data.SqlServer.Data.Models; - -/// -/// Example. -/// -public class Example : BaseEntity -{ - /// - /// Name. - /// - public virtual string Name { get; set; } = null!; -} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContext.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContext.cs deleted file mode 100644 index 81776d33..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContext.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Nano.Data; -using Nano.Data.Abstractions.Config; - -namespace Console.Data.SqlServer.Data; - -/// -public class SqlServerDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) - : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContextFactory.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContextFactory.cs deleted file mode 100644 index 83b89567..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContextFactory.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Nano.Data; -using Nano.Data.SqlServer; - -namespace Console.Data.SqlServer.Data; - -/// -public class SqlServerDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Dockerfile.Local b/Console.Data.SqlServer/Console.Data.SqlServer/Dockerfile.Local deleted file mode 100644 index 14a7956b..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260311103638_Initial.Designer.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260311103638_Initial.Designer.cs deleted file mode 100644 index 49ffd9e2..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260311103638_Initial.Designer.cs +++ /dev/null @@ -1,574 +0,0 @@ -// -using System; -using Console.Data.SqlServer.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Console.Data.SqlServer.Migrations -{ - [DbContext(typeof(SqlServerDbContext))] - [Migration("20260311103638_Initial")] - partial class Initial - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Console.Data.SqlServer.Data.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("nvarchar(max)"); - - b.Property("Xml") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityTypeName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("State") - .HasColumnType("int"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.HasIndex("State"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("nvarchar(max)"); - - b.Property("OldValue") - .HasColumnType("nvarchar(max)"); - - b.Property("ParentId") - .HasColumnType("uniqueidentifier"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(450)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique() - .HasFilter("[Email] IS NOT NULL"); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.HasIndex("PhoneNumber") - .IsUnique() - .HasFilter("[PhoneNumber] IS NOT NULL"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetimeoffset"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260311103638_Initial.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260311103638_Initial.cs deleted file mode 100644 index c2847f6d..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260311103638_Initial.cs +++ /dev/null @@ -1,477 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Console.Data.SqlServer.Migrations -{ - /// - public partial class Initial : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "__EFAudit", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - CreatedBy = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EntitySetName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EntityTypeName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - State = table.Column(type: "int", nullable: false), - StateName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - RequestId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAudit", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFDataProtectionKeys", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - FriendlyName = table.Column(type: "nvarchar(max)", nullable: true), - Xml = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRole", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRole", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUser", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), - UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - EmailConfirmed = table.Column(type: "bit", nullable: false), - PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), - SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), - ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), - PhoneNumber = table.Column(type: "nvarchar(450)", nullable: true), - PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), - TwoFactorEnabled = table.Column(type: "bit", nullable: false), - LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), - LockoutEnabled = table.Column(type: "bit", nullable: false), - AccessFailedCount = table.Column(type: "int", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUser", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Example", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Example", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "__EFAuditProperties", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - ParentId = table.Column(type: "uniqueidentifier", nullable: false), - PropertyName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - RelationName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NewValue = table.Column(type: "nvarchar(max)", nullable: true), - OldValue = table.Column(type: "nvarchar(max)", nullable: true), - IsDeleted = table.Column(type: "bigint", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFAuditProperties", x => x.Id); - table.ForeignKey( - name: "FK___EFAuditProperties___EFAudit_ParentId", - column: x => x.ParentId, - principalTable: "__EFAudit", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityRoleClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - RoleId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityApiKey", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Hash = table.Column(type: "nvarchar(max)", nullable: false), - CreatedAt = table.Column(type: "datetimeoffset", nullable: false), - RevokedAt = table.Column(type: "datetimeoffset", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserChangeData", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - NewEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), - NewPhoneNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserClaim", - columns: table => new - { - Id = table.Column(type: "int", nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - UserId = table.Column(type: "uniqueidentifier", nullable: false), - ClaimType = table.Column(type: "nvarchar(max)", nullable: true), - ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserLogin", - columns: table => new - { - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), - ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), - UserId = table.Column(type: "uniqueidentifier", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); - table.ForeignKey( - name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRefreshToken", - columns: table => new - { - Id = table.Column(type: "uniqueidentifier", nullable: false), - IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), - AppId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - Value = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), - ExpireAt = table.Column(type: "datetimeoffset", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); - table.ForeignKey( - name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", - column: x => x.IdentityUserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserRole", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - RoleId = table.Column(type: "uniqueidentifier", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", - column: x => x.RoleId, - principalTable: "__EFIdentityRole", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "__EFIdentityUserToken", - columns: table => new - { - UserId = table.Column(type: "uniqueidentifier", nullable: false), - LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), - Name = table.Column(type: "nvarchar(450)", nullable: false), - Value = table.Column(type: "nvarchar(max)", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); - table.ForeignKey( - name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", - column: x => x.UserId, - principalTable: "__EFIdentityUser", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_CreatedBy", - table: "__EFAudit", - column: "CreatedBy"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_EntityTypeName", - table: "__EFAudit", - column: "EntityTypeName"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_RequestId", - table: "__EFAudit", - column: "RequestId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAudit_State", - table: "__EFAudit", - column: "State"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_ParentId", - table: "__EFAuditProperties", - column: "ParentId"); - - migrationBuilder.CreateIndex( - name: "IX___EFAuditProperties_PropertyName", - table: "__EFAuditProperties", - column: "PropertyName"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_IdentityUserId", - table: "__EFIdentityApiKey", - column: "IdentityUserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityApiKey_RevokedAt", - table: "__EFIdentityApiKey", - column: "RevokedAt"); - - migrationBuilder.CreateIndex( - name: "RoleNameIndex", - table: "__EFIdentityRole", - column: "NormalizedName", - unique: true, - filter: "[NormalizedName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityRoleClaim_RoleId", - table: "__EFIdentityRoleClaim", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "EmailIndex", - table: "__EFIdentityUser", - column: "NormalizedEmail"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_Email", - table: "__EFIdentityUser", - column: "Email", - unique: true, - filter: "[Email] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_IsActive", - table: "__EFIdentityUser", - column: "IsActive"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUser_PhoneNumber", - table: "__EFIdentityUser", - column: "PhoneNumber", - unique: true, - filter: "[PhoneNumber] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "UserNameIndex", - table: "__EFIdentityUser", - column: "NormalizedUserName", - unique: true, - filter: "[NormalizedUserName] IS NOT NULL"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserChangeData_IdentityUserId", - table: "__EFIdentityUserChangeData", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserClaim_UserId", - table: "__EFIdentityUserClaim", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserLogin_UserId", - table: "__EFIdentityUserLogin", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRefreshToken_ExpireAt", - table: "__EFIdentityUserRefreshToken", - column: "ExpireAt"); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId", - table: "__EFIdentityUserRefreshToken", - column: "IdentityUserId", - unique: true); - - migrationBuilder.CreateIndex( - name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", - table: "__EFIdentityUserRefreshToken", - columns: new[] { "IdentityUserId", "AppId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX___EFIdentityUserRole_RoleId", - table: "__EFIdentityUserRole", - column: "RoleId"); - - migrationBuilder.CreateIndex( - name: "IX_Example_CreatedAt", - table: "Example", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Example_IsDeleted", - table: "Example", - column: "IsDeleted"); - - migrationBuilder.CreateIndex( - name: "IX_Example_Name", - table: "Example", - column: "Name"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "__EFAuditProperties"); - - migrationBuilder.DropTable( - name: "__EFDataProtectionKeys"); - - migrationBuilder.DropTable( - name: "__EFIdentityApiKey"); - - migrationBuilder.DropTable( - name: "__EFIdentityRoleClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserChangeData"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserClaim"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserLogin"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRefreshToken"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUserToken"); - - migrationBuilder.DropTable( - name: "Example"); - - migrationBuilder.DropTable( - name: "__EFAudit"); - - migrationBuilder.DropTable( - name: "__EFIdentityRole"); - - migrationBuilder.DropTable( - name: "__EFIdentityUser"); - } - } -} diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs deleted file mode 100644 index f6aecaed..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs +++ /dev/null @@ -1,571 +0,0 @@ -// -using System; -using Console.Data.SqlServer.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace Console.Data.SqlServer.Migrations -{ - [DbContext(typeof(SqlServerDbContext))] - partial class SqlServerDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "9.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("Console.Data.SqlServer.Data.Models.Example", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(0L); - - b.Property("Name") - .IsRequired() - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedAt"); - - b.HasIndex("IsDeleted"); - - b.HasIndex("Name"); - - b.ToTable("Example"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("FriendlyName") - .HasColumnType("nvarchar(max)"); - - b.Property("Xml") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("__EFDataProtectionKeys", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex") - .HasFilter("[NormalizedName] IS NOT NULL"); - - b.ToTable("__EFIdentityRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityRoleClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("nvarchar(max)"); - - b.Property("ClaimValue") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserClaim", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderKey") - .HasColumnType("nvarchar(450)"); - - b.Property("ProviderDisplayName") - .HasColumnType("nvarchar(max)"); - - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("__EFIdentityUserLogin", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("RoleId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("__EFIdentityUserRole", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("uniqueidentifier"); - - b.Property("LoginProvider") - .HasColumnType("nvarchar(450)"); - - b.Property("Name") - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .HasColumnType("nvarchar(max)"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("__EFIdentityUserToken", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("CreatedBy") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntitySetName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EntityTypeName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("RequestId") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("State") - .HasColumnType("int"); - - b.Property("StateName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("CreatedBy"); - - b.HasIndex("EntityTypeName"); - - b.HasIndex("RequestId"); - - b.HasIndex("State"); - - b.ToTable("__EFAudit", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("IsDeleted") - .HasColumnType("bigint"); - - b.Property("NewValue") - .HasColumnType("nvarchar(max)"); - - b.Property("OldValue") - .HasColumnType("nvarchar(max)"); - - b.Property("ParentId") - .HasColumnType("uniqueidentifier"); - - b.Property("PropertyName") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RelationName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ParentId"); - - b.HasIndex("PropertyName"); - - b.ToTable("__EFAuditProperties", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("CreatedAt") - .HasColumnType("datetimeoffset"); - - b.Property("Hash") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("RevokedAt") - .HasColumnType("datetimeoffset"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId"); - - b.HasIndex("RevokedAt"); - - b.ToTable("__EFIdentityApiKey", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("NewEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NewPhoneNumber") - .HasMaxLength(20) - .HasColumnType("nvarchar(20)"); - - b.HasKey("Id"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); - - b.ToTable("__EFIdentityUserChangeData", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AccessFailedCount") - .HasColumnType("int"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("bit"); - - b.Property("IsActive") - .ValueGeneratedOnAdd() - .HasColumnType("bit") - .HasDefaultValue(true); - - b.Property("LockoutEnabled") - .HasColumnType("bit"); - - b.Property("LockoutEnd") - .HasColumnType("datetimeoffset"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("PasswordHash") - .HasColumnType("nvarchar(max)"); - - b.Property("PhoneNumber") - .HasColumnType("nvarchar(450)"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("bit"); - - b.Property("SecurityStamp") - .HasColumnType("nvarchar(max)"); - - b.Property("TwoFactorEnabled") - .HasColumnType("bit"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("Email") - .IsUnique() - .HasFilter("[Email] IS NOT NULL"); - - b.HasIndex("IsActive"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex") - .HasFilter("[NormalizedUserName] IS NOT NULL"); - - b.HasIndex("PhoneNumber") - .IsUnique() - .HasFilter("[PhoneNumber] IS NOT NULL"); - - b.ToTable("__EFIdentityUser", (string)null); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uniqueidentifier"); - - b.Property("AppId") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.Property("ExpireAt") - .HasColumnType("datetimeoffset"); - - b.Property("IdentityUserId") - .HasColumnType("uniqueidentifier"); - - b.Property("Value") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("nvarchar(256)"); - - b.HasKey("Id"); - - b.HasIndex("ExpireAt"); - - b.HasIndex("IdentityUserId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId"); - - b.HasIndex("IdentityUserId", "AppId") - .IsUnique() - .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); - - b.ToTable("__EFIdentityUserRefreshToken", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => - { - b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") - .WithMany("Properties") - .HasForeignKey("ParentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithMany() - .HasForeignKey("IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => - { - b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") - .WithOne() - .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", "IdentityUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityUser"); - }); - - modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => - { - b.Navigation("Properties"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Program.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Program.cs deleted file mode 100644 index ac3119ae..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Program.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Console.Data.SqlServer.Data; -using Nano.App.Console; -using Nano.Data.Extensions; -using Nano.Data.SqlServer; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoData(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Properties/InternalsVisibleTo.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Properties/InternalsVisibleTo.cs deleted file mode 100644 index a42febc8..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Data.SqlServer")] \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Workers/ExampleWorker.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Workers/ExampleWorker.cs deleted file mode 100644 index 35606102..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/Workers/ExampleWorker.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Console.Data.SqlServer.Data.Models; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using Nano.Data.Abstractions; - -namespace Console.Data.SqlServer.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) -{ - private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); - - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - var example = await this.repository - .AddAsync(new Example - { - Name = "name" - }, cancellationToken); - - await this.repository - .SaveChangesAsync(cancellationToken); - - example = await this.repository - .GetAsync(example.Id, cancellationToken); - - System.Console.WriteLine($"The example name is: '{example!.Name}'"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Development.json b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Development.json deleted file mode 100644 index c4f4436e..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Development.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "Data": { - "UseMigrateDatabase": true, - "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" - } -} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Production.json b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Staging.json b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.json b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.json deleted file mode 100644 index f52b7fe7..00000000 --- a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null - } -} \ No newline at end of file diff --git a/Console.Data.SqlServer/Dockerfile b/Console.Data.SqlServer/Dockerfile deleted file mode 100644 index 1f2cd851..00000000 --- a/Console.Data.SqlServer/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Console.Data.SqlServer/README.md b/Console.Data.SqlServer/README.md deleted file mode 100644 index 335cb5bd..00000000 --- a/Console.Data.SqlServer/README.md +++ /dev/null @@ -1,208 +0,0 @@ -# Console.Data.SqlServer - -> _Nano Console application with sql server data._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented -for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-mappings), -and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#data-context). - -The worker creates and retreives a `Example` entity using the Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data#repositories). Run the -application and observe the console output generated by the `ExampleWorker`. - -> 📖 Learn more about **[Nano.Data.SqlServer](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqlServer)**. - -## Registration -The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoData(); -}) -... -``` - -Also, an initial migration has been added to the project. - -```powershell -dotnet ef migrations add Initial --project Console.Data.SqlServer -``` - -## Configuration -Configured the application with the necessary data setup. - -```json -"Data": { - "BatchSize": 25, - "BulkBatchSize": 500, - "BulkBatchDelay": 1000, - "QueryRetryCount": 0, - "UseLazyLoading": false, - "UseCreateDatabase": false, - "UseMigrateDatabase": false, - "UseSoftDeletetion": false, - "UseSensitiveDataLogging": false, - "UseAudit": false, - "QuerySplittingBehavior": "SingleQuery", - "DefaultCollation": null, - "ConnectionString": null, - "Repository": { - "UseAutoSave": false, - "QueryIncludeDepth": 4 - }, - "Identity": null, - "ConnectionPool": null, - "HealthCheck": null -} -``` - -...and `appsettings.Development.json` - -```json -"Data": { - "UseMigrateDatabase": true, - "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" -} -``` - -## Docker Compose -Added Sql Server as a service dependency in `docker-compose.yml`. - -```yaml -services: - console.data.sqlserver: - depends_on: - - database - - database: - image: mcr.microsoft.com/mssql/server:2022-latest - ports: - - 1433:1433 - networks: - - network - environment: - SA_PASSWORD: myPassword_123 - ACCEPT_EULA: Y - MSSQL_PID: Developer -``` - -## Kubernetes -Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. - -```json -spec: - jobTemplate: - spec: - template: - spec: - containers: - env: - - name: Data__ConnectionString - valueFrom: - secretKeyRef: - name: %SERVICE_NAME%-secret - key: data-connectionstring -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - DATA_HOST: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_HOST || secrets.STAGING_SQLSERVER_HOST }} - DATA_NAME: nanoDb - DATA_USER: api-data-sqlserver-user - DATA_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_NANO_DB_PASSWORD || secrets.STAGING_SQLSERVER_NANO_DB_PASSWORD }} - DATA_ADMIN_USER: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_USER || secrets.STAGING_SQLSERVER_ADMIN_USER }} - DATA_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQLSERVER_ADMIN_PASSWORD || secrets.STAGING_SQLSERVER_ADMIN_PASSWORD }} - DATA_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_USER }};Password=${{ env.DATA_PASSWORD }}; - DATA_MIGRATION_CONNECTIONSTRING: Server=${{ env.DATA_HOST }},${{ vars.DATA_SQLSERVER_PORT }};Database=${{ env.DATA_NAME }};User Id=${{ env.DATA_ADMIN_USER }};Password=${{ env.DATA_ADMIN_PASSWORD }}; -``` - -Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. - -```yaml -- name: Database Migration - shell: pwsh - run: | - dotnet ef database update ` - --no-build ` - --startup-project $env:APP_NAME ` - --connection "$env:DATA_MIGRATION_CONNECTIONSTRING" `; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo apt-get update - sudo apt-get install -y mssql-tools unixodbc-dev - - $loginExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:DATA_USER';" - - if ($loginExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d master ` - -Q "CREATE LOGIN [$env:DATA_USER] WITH PASSWORD = '$env:DATA_PASSWORD';" - } - - $userExists = sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -h -1 ` - -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:DATA_USER';" - - if ($userExists -eq 0) - { - sqlcmd ` - -S "$env:DATA_HOST,$env:DATA_SQLSERVER_PORT" ` - -U $env:DATA_ADMIN_USER ` - -P $env:DATA_ADMIN_PASSWORD ` - -d $env:DATA_NAME ` - -Q "CREATE USER [$env:DATA_USER] FOR LOGIN [$env:DATA_USER]; - ALTER ROLE db_datareader ADD MEMBER [$env:DATA_USER]; - ALTER ROLE db_datawriter ADD MEMBER [$env:DATA_USER];" - } -``` - -Last, the application connectionstring must be added in a secret in Kuberntes. The `Kubernetes Deploy` step has been updated with the following. - -```yaml -sudo kubectl create secret generic $env:SERVICE_NAME-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; -if ($LastExitCode -ne 0) -{ - throw "error"; -}; -``` diff --git a/Console.Eventing.RabbitMq/.docker/docker-compose.dcproj b/Console.Eventing.RabbitMq/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Eventing.RabbitMq/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/.docker/docker-compose.yml b/Console.Eventing.RabbitMq/.docker/docker-compose.yml deleted file mode 100644 index 466eb2f3..00000000 --- a/Console.Eventing.RabbitMq/.docker/docker-compose.yml +++ /dev/null @@ -1,31 +0,0 @@ -services: - console.eventing.rabbitmq: - image: console.eventing.rabbitmq - restart: on-failure - build: - context: ../Console.Eventing.RabbitMq - dockerfile: "Dockerfile.Local" - depends_on: - - eventing - networks: - - network - - eventing: - image: rabbitmq:management - hostname: rabbitmq - ports: - - 5671:5671 - - 5672:5672 - - 15671:15671 - - 15672:15672 - networks: - - network - environment: - RABBITMQ_DEFAULT_USER: rabbitmq_user - RABBITMQ_DEFAULT_PASS: password - RABBITMQ_DEFAULT_VHOST: / - -networks: - network: - name: network - external: true diff --git a/Console.Eventing.RabbitMq/.dockerignore b/Console.Eventing.RabbitMq/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Eventing.RabbitMq/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Eventing.RabbitMq/.github/config/slack.yml b/Console.Eventing.RabbitMq/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Eventing.RabbitMq/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml b/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 8db75f9e..00000000 --- a/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Eventing.RabbitMq - IMAGE_NAME: console.eventing.rabbitmq - SERVICE_NAME: console-eventing-rabbitmq - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Eventing.RabbitMq/.gitignore b/Console.Eventing.RabbitMq/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Eventing.RabbitMq/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/.kubernetes/configmap.yaml b/Console.Eventing.RabbitMq/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Eventing.RabbitMq/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml b/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml deleted file mode 100644 index 5113e567..00000000 --- a/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - env: - - name: Eventing__Credentials__Secret - valueFrom: - secretKeyRef: - name: rabbitmq - key: rabbitmq-password - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Properties/DoNotParallelize.cs b/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Tests.Console.Eventing.RabbitMq.csproj b/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Tests.Console.Eventing.RabbitMq.csproj deleted file mode 100644 index acdf942c..00000000 --- a/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Tests.Console.Eventing.RabbitMq.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.sln b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.sln deleted file mode 100644 index a2017d3b..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Eventing.RabbitMq", "Console.Eventing.RabbitMq\Console.Eventing.RabbitMq.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Eventing.RabbitMq", ".tests\Tests.Console.Eventing.RabbitMq\Tests.Console.Eventing.RabbitMq.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing", "..\..\Nano.Library\Nano.Eventing\Nano.Eventing.csproj", "{A8E623BC-70EA-3CC8-AFAD-797F006C4A41}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.RabbitMq", "..\..\Nano.Library\Nano.Eventing.RabbitMq\Nano.Eventing.RabbitMq.csproj", "{0789C863-B371-F968-B9C9-5F3CF3DD9897}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.Build.0 = Release|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {A8E623BC-70EA-3CC8-AFAD-797F006C4A41} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0789C863-B371-F968-B9C9-5F3CF3DD9897} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.csproj b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.csproj deleted file mode 100644 index 5b475559..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.csproj +++ /dev/null @@ -1,49 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Dockerfile.Local b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Dockerfile.Local deleted file mode 100644 index 0cda920a..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/EventingHandler.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/EventingHandler.cs deleted file mode 100644 index ab33b2af..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/EventingHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Threading.Tasks; -using Console.Eventing.RabbitMq.Eventing.Models; -using Nano.Eventing.Abstractions; - -namespace Console.Eventing.RabbitMq.Eventing; - -/// -/// Eventing Handler. -/// -public class EventingHandler : BaseEventHandler -{ - /// - /// Callback. - /// - /// The . - /// Whether the event is retrying. - /// Nothing. - public override async Task CallbackAsync(EventModel @event, bool isRetrying) - { - await Task.CompletedTask; - - System.Console.WriteLine(@event.Text); - } -} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/Models/EventModel.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/Models/EventModel.cs deleted file mode 100644 index 7e7cd89b..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/Models/EventModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Console.Eventing.RabbitMq.Eventing.Models; - -/// -/// Event Model. -/// -public class EventModel -{ - /// - /// Text. - /// - public virtual string? Text { get; set; } -} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Program.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Program.cs deleted file mode 100644 index d5cafa47..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Console; -using Nano.Eventing.Extensions; -using Nano.Eventing.RabbitMq; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoEventing(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs deleted file mode 100644 index ee41940e..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Eventing.RabbitMq")] \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Workers/ExampleWorker.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Workers/ExampleWorker.cs deleted file mode 100644 index 0ecea4f0..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Workers/ExampleWorker.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using Nano.Eventing.Abstractions; -using System.Threading; -using System.Threading.Tasks; -using Console.Eventing.RabbitMq.Eventing.Models; - -namespace Console.Eventing.RabbitMq.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IEventing eventing) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await eventing - .PublishAsync(new EventModel - { - Text = "Testing eventing" - }, cancellationToken: cancellationToken); - - await Task.Delay(500, cancellationToken); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Development.json b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Production.json b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Staging.json b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.json b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.json deleted file mode 100644 index b829b3fe..00000000 --- a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Eventing": { - "Host": "rabbitmq", - "VHost": "/", - "Port": 5672, - "UseSsl": false, - "Timeout": "00:00:30", - "Heartbeat": 60, - "PrefetchCount": 50, - "Credentials": { - "Id": "rabbitmq_user", - "Secret": "password" - } - } -} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Dockerfile b/Console.Eventing.RabbitMq/Dockerfile deleted file mode 100644 index 986cf2d4..00000000 --- a/Console.Eventing.RabbitMq/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/README.md b/Console.Eventing.RabbitMq/README.md deleted file mode 100644 index bba70412..00000000 --- a/Console.Eventing.RabbitMq/README.md +++ /dev/null @@ -1,110 +0,0 @@ -# Console.Eventing.RabbitMq - -> _Nano console application with rabbitmq eventing._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -Although event handling is not typically associated with console applications, there are valid scenarios where event-driven architecture makes sense -in a console worker. For example, a worker might retrieve data from a database or an API client and publish events for distributed processing across services. - -Run the application and observe how an `EventModel` instance is published by the `ExampleWorker` and handled by the `EventingHandler`. - -Check the console output for the message: `Testing eventing`. -This message is written by the `EventHandler` when the event is successfully received. - -You can access the RabbitMQ management interface here: **[http://localhost:15672](http://localhost:15672)**. From there, you can monitor the messages -being published and consumed in real time. - -> 📖 Learn more about **[Nano.Eventing.RabbitMq](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Eventing.RabbitMq)**. - -## Registration -The following eventing provider has been registered using `ConfigureServices(...)` in `Program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoEventing(); -}) -... -``` - -## Configuration -Configured the application with the necessary eventing setup. - -```json -"App": { -"Eventing": { - "Host": "rabbitmq", - "VHost": "/", - "Port": 5672, - "UseSsl": false, - "Timeout": "00:00:30", - "Heartbeat": 60, - "PrefetchCount": 50, - "Credentials": { - "Id": "rabbitmq_user", - "Secret": "password" - } -} -``` - -## Docker Compose -Added RabbitMQ as a service dependency in `docker-compose.yml`. - -```yaml -services: - api.eventing.rabbitmq: - depends_on: - - eventing - - eventing: - image: rabbitmq:management - hostname: rabbitmq - ports: - - 5671:5671 - - 5672:5672 - - 15671:15671 - - 15672:15672 - networks: - - network - environment: - RABBITMQ_DEFAULT_USER: rabbitmq_user - RABBITMQ_DEFAULT_PASS: password - RABBITMQ_DEFAULT_VHOST: "/" - -``` -## Kubernetes -Added the `rabbitmq` secret for password to the `cronjob.yaml`. - -```json -spec: - template: - spec: - containers: - env: - - name: Eventing__Credentials__Secret - valueFrom: - secretKeyRef: - name: rabbitmq - key: rabbitmq-password -``` - -> ⚠️ The `rabbitmq` secret is created alongside the **[Nano Azure Kubernetes Eventing](https://github.com/Nano-Core/Nano.Azure.Kubernetes/tree/master/Nano.Azure.Kubernetes.RabbitMQ)** diff --git a/Console.ExceptionHandling/.docker/docker-compose.dcproj b/Console.ExceptionHandling/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.ExceptionHandling/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.ExceptionHandling/.docker/docker-compose.yml b/Console.ExceptionHandling/.docker/docker-compose.yml deleted file mode 100644 index 25043431..00000000 --- a/Console.ExceptionHandling/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.exceptionhandling: - image: console.exceptionhandling - restart: on-failure - build: - context: ../Console.ExceptionHandling - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.ExceptionHandling/.dockerignore b/Console.ExceptionHandling/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.ExceptionHandling/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.ExceptionHandling/.github/config/slack.yml b/Console.ExceptionHandling/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.ExceptionHandling/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml b/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml deleted file mode 100644 index bade116a..00000000 --- a/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.ExceptionHandling - IMAGE_NAME: console.exceptionhandling - SERVICE_NAME: console-exceptionhandling - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.ExceptionHandling/.gitignore b/Console.ExceptionHandling/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.ExceptionHandling/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.ExceptionHandling/.kubernetes/configmap.yaml b/Console.ExceptionHandling/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.ExceptionHandling/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.ExceptionHandling/.kubernetes/cronjob.yaml b/Console.ExceptionHandling/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.ExceptionHandling/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Properties/DoNotParallelize.cs b/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Tests.Console.ExceptionHandling.csproj b/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Tests.Console.ExceptionHandling.csproj deleted file mode 100644 index 1e70f784..00000000 --- a/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Tests.Console.ExceptionHandling.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.ExceptionHandling/Console.ExceptionHandling.sln b/Console.ExceptionHandling/Console.ExceptionHandling.sln deleted file mode 100644 index 01421e59..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling.sln +++ /dev/null @@ -1,123 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 d18.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.ExceptionHandling", "Console.ExceptionHandling\Console.ExceptionHandling.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.ExceptionHandling", ".tests\Tests.Console.ExceptionHandling\Tests.Console.ExceptionHandling.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Console.ExceptionHandling.csproj b/Console.ExceptionHandling/Console.ExceptionHandling/Console.ExceptionHandling.csproj deleted file mode 100644 index e0dfc0c1..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/Console.ExceptionHandling.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Dockerfile.Local b/Console.ExceptionHandling/Console.ExceptionHandling/Dockerfile.Local deleted file mode 100644 index 9a65fa1d..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.ExceptionHandling.dll"] \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Program.cs b/Console.ExceptionHandling/Console.ExceptionHandling/Program.cs deleted file mode 100644 index 1aa684bd..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Console; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Properties/InternalsVisibleTo.cs b/Console.ExceptionHandling/Console.ExceptionHandling/Properties/InternalsVisibleTo.cs deleted file mode 100644 index cbd1f000..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.ExceptionHandling")] \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Workers/ExampleWorker.cs b/Console.ExceptionHandling/Console.ExceptionHandling/Workers/ExampleWorker.cs deleted file mode 100644 index 4c23274e..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/Workers/ExampleWorker.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; - -namespace Console.ExceptionHandling.Workers; - -/// -/// Example Worker. -/// -/// The . -public class ExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.CompletedTask; - - throw new Exception("Example Worker Exception..."); - } -} \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Development.json b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Production.json b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Staging.json b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.json b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.json deleted file mode 100644 index c7b251f2..00000000 --- a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - } -} \ No newline at end of file diff --git a/Console.ExceptionHandling/Dockerfile b/Console.ExceptionHandling/Dockerfile deleted file mode 100644 index e7bc637d..00000000 --- a/Console.ExceptionHandling/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.ExceptionHandling.dll"] \ No newline at end of file diff --git a/Console.ExceptionHandling/README.md b/Console.ExceptionHandling/README.md deleted file mode 100644 index 4735fb33..00000000 --- a/Console.ExceptionHandling/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Console.ExceptionHandling - -> _Nano Console application showing exception handling._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates exception handling for a console application. - -Run the application and observe the `Exception` being thrown in the worker `OnStartAsync()`. - -> 📖 Learn more about **[Nano Exception Handling](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Console#exception-handling)**. diff --git a/Console.Localization/.docker/docker-compose.dcproj b/Console.Localization/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Localization/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Localization/.docker/docker-compose.yml b/Console.Localization/.docker/docker-compose.yml deleted file mode 100644 index 26aa2436..00000000 --- a/Console.Localization/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.localization: - image: console.localization - restart: on-failure - build: - context: ../Console.Localization - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.Localization/.dockerignore b/Console.Localization/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Localization/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Localization/.github/config/slack.yml b/Console.Localization/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Localization/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Localization/.github/workflows/build-and-deploy.yml b/Console.Localization/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 3a824b45..00000000 --- a/Console.Localization/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Localization - IMAGE_NAME: console.localization - SERVICE_NAME: console-localization - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Localization/.gitignore b/Console.Localization/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Localization/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Localization/.kubernetes/configmap.yaml b/Console.Localization/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Localization/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Localization/.kubernetes/cronjob.yaml b/Console.Localization/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.Localization/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Localization/.tests/Tests.Console.Localization/Properties/DoNotParallelize.cs b/Console.Localization/.tests/Tests.Console.Localization/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Localization/.tests/Tests.Console.Localization/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Localization/.tests/Tests.Console.Localization/Tests.Console.Localization.csproj b/Console.Localization/.tests/Tests.Console.Localization/Tests.Console.Localization.csproj deleted file mode 100644 index 90bb12ff..00000000 --- a/Console.Localization/.tests/Tests.Console.Localization/Tests.Console.Localization.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Localization/Console.Localization.sln b/Console.Localization/Console.Localization.sln deleted file mode 100644 index a82eed0d..00000000 --- a/Console.Localization/Console.Localization.sln +++ /dev/null @@ -1,123 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 d18.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Localization", "Console.Localization\Console.Localization.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Localization", ".tests\Tests.Console.Localization\Tests.Console.Localization.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Localization/Console.Localization/Console.Localization.csproj b/Console.Localization/Console.Localization/Console.Localization.csproj deleted file mode 100644 index e0dfc0c1..00000000 --- a/Console.Localization/Console.Localization/Console.Localization.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Localization/Console.Localization/Dockerfile.Local b/Console.Localization/Console.Localization/Dockerfile.Local deleted file mode 100644 index 643806eb..00000000 --- a/Console.Localization/Console.Localization/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Localization.dll"] \ No newline at end of file diff --git a/Console.Localization/Console.Localization/Program.cs b/Console.Localization/Console.Localization/Program.cs deleted file mode 100644 index 1aa684bd..00000000 --- a/Console.Localization/Console.Localization/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Console; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Localization/Console.Localization/Properties/InternalsVisibleTo.cs b/Console.Localization/Console.Localization/Properties/InternalsVisibleTo.cs deleted file mode 100644 index ad479afc..00000000 --- a/Console.Localization/Console.Localization/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Localization")] \ No newline at end of file diff --git a/Console.Localization/Console.Localization/Workers/ExampleWorker.cs b/Console.Localization/Console.Localization/Workers/ExampleWorker.cs deleted file mode 100644 index 2c0f3a21..00000000 --- a/Console.Localization/Console.Localization/Workers/ExampleWorker.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using System; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; - -namespace Console.Localization.Workers; - -/// -/// Example Worker. -/// -/// The . -public class ExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.CompletedTask; - - System.Console.WriteLine($"Culture: {CultureInfo.CurrentCulture.Name}"); - System.Console.WriteLine($"DateTime: {DateTime.Now}"); - System.Console.WriteLine($"Long date: {DateTime.Now:D}"); - System.Console.WriteLine($"Number: {1234567.89.ToString(CultureInfo.InvariantCulture)}"); - System.Console.WriteLine($"Currency: {1234.56m:C}"); - System.Console.WriteLine($"Percent: {0.256:P}"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Localization/Console.Localization/appsettings.Development.json b/Console.Localization/Console.Localization/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Localization/Console.Localization/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Localization/Console.Localization/appsettings.Production.json b/Console.Localization/Console.Localization/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Localization/Console.Localization/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Localization/Console.Localization/appsettings.Staging.json b/Console.Localization/Console.Localization/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Localization/Console.Localization/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Localization/Console.Localization/appsettings.json b/Console.Localization/Console.Localization/appsettings.json deleted file mode 100644 index 80cf620a..00000000 --- a/Console.Localization/Console.Localization/appsettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Localization": { - "DefaultCulture": "fr-FR" - } - } -} \ No newline at end of file diff --git a/Console.Localization/Dockerfile b/Console.Localization/Dockerfile deleted file mode 100644 index 82e9da78..00000000 --- a/Console.Localization/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Localization.dll"] \ No newline at end of file diff --git a/Console.Localization/README.md b/Console.Localization/README.md deleted file mode 100644 index 094e233d..00000000 --- a/Console.Localization/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Console.Localization - -> _Nano Console application with localization configured._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates configuring `Localization` in a Nano console application. - -Run the application and observe how the configured localization is used when printing out `DateTimeOffset.Now`. - -> 📖 Learn more about **[Nano Console Localization](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Console#localization)**. - -## Configuration -```json -"App": { - "Localization": { - "DefaultCulture": "fr-FR" - } -} -``` \ No newline at end of file diff --git a/Console.Logging.Log4Net/.docker/docker-compose.dcproj b/Console.Logging.Log4Net/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Logging.Log4Net/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Logging.Log4Net/.docker/docker-compose.yml b/Console.Logging.Log4Net/.docker/docker-compose.yml deleted file mode 100644 index 61a250c9..00000000 --- a/Console.Logging.Log4Net/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.logging.log4net: - image: console.logging.log4net - restart: on-failure - build: - context: ../Console.Logging.Log4Net - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.Logging.Log4Net/.dockerignore b/Console.Logging.Log4Net/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Logging.Log4Net/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Logging.Log4Net/.github/config/slack.yml b/Console.Logging.Log4Net/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Logging.Log4Net/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml b/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 27242720..00000000 --- a/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Logging.Log4Net - IMAGE_NAME: console.logging.log4net - SERVICE_NAME: console-logging-log4net - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Logging.Log4Net/.gitignore b/Console.Logging.Log4Net/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Logging.Log4Net/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Logging.Log4Net/.kubernetes/configmap.yaml b/Console.Logging.Log4Net/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Logging.Log4Net/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Logging.Log4Net/.kubernetes/cronjob.yaml b/Console.Logging.Log4Net/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.Logging.Log4Net/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Properties/DoNotParallelize.cs b/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Tests.Console.Logging.Log4Net.csproj b/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Tests.Console.Logging.Log4Net.csproj deleted file mode 100644 index e4d56afc..00000000 --- a/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Tests.Console.Logging.Log4Net.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net.sln b/Console.Logging.Log4Net/Console.Logging.Log4Net.sln deleted file mode 100644 index 32e304d3..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Logging.Log4Net", "Console.Logging.Log4Net\Console.Logging.Log4Net.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Logging.Log4Net", ".tests\Tests.Console.Logging.Log4Net\Tests.Console.Logging.Log4Net.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Log4Net", "..\..\Nano.Library\Nano.Logging.Log4Net\Nano.Logging.Log4Net.csproj", "{EF1890AC-F965-E660-35B6-4E181BEB3ED8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.Build.0 = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {EF1890AC-F965-E660-35B6-4E181BEB3ED8} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {68215D72-880F-683C-BFEE-ED19A425A5F2} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Console.Logging.Log4Net.csproj b/Console.Logging.Log4Net/Console.Logging.Log4Net/Console.Logging.Log4Net.csproj deleted file mode 100644 index 15fccebd..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/Console.Logging.Log4Net.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Dockerfile.Local b/Console.Logging.Log4Net/Console.Logging.Log4Net/Dockerfile.Local deleted file mode 100644 index f57f1262..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Program.cs b/Console.Logging.Log4Net/Console.Logging.Log4Net/Program.cs deleted file mode 100644 index 3e6a977b..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Console; -using Nano.Logging.Extensions; -using Nano.Logging.Log4Net; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoLogging(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Properties/InternalsVisibleTo.cs b/Console.Logging.Log4Net/Console.Logging.Log4Net/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 2713a2c2..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Logging.Log4Net")] \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Workers/ExampleWorker.cs b/Console.Logging.Log4Net/Console.Logging.Log4Net/Workers/ExampleWorker.cs deleted file mode 100644 index ef9929db..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/Workers/ExampleWorker.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; - -namespace Console.Logging.Log4Net.Workers; - -/// -/// Example Worker. -/// -/// The . -public class ExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.Delay(500, cancellationToken); - - this.logger - .LogWarning("warning from worker"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Development.json b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Production.json b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Staging.json b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.json b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.json deleted file mode 100644 index 251db522..00000000 --- a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] - } -} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Dockerfile b/Console.Logging.Log4Net/Dockerfile deleted file mode 100644 index af52ba2e..00000000 --- a/Console.Logging.Log4Net/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Console.Logging.Log4Net/README.md b/Console.Logging.Log4Net/README.md deleted file mode 100644 index 06204354..00000000 --- a/Console.Logging.Log4Net/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Console.Logging.Log4Net - -> _Nano Console application with Log4Net logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates logging with Log4Net for a console application. - -Run the application and observe how `ExampleWorker` logs a warning to the console. Also note the `LogLevelOverrides` configuration, -where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. - -> 📖 Learn more about **[Nano.Logging.Log4Net](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Log4Net)**. - -## Registration -The following logging has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoLogging(); -}) -... -``` - -## Configuration -Configured the application with the necessary logging setup. - -```json -"Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] -} -``` diff --git a/Console.Logging.Microsoft/.docker/docker-compose.dcproj b/Console.Logging.Microsoft/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Logging.Microsoft/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Logging.Microsoft/.docker/docker-compose.yml b/Console.Logging.Microsoft/.docker/docker-compose.yml deleted file mode 100644 index 98d18205..00000000 --- a/Console.Logging.Microsoft/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.logging.microsoft: - image: console.logging.microsoft - restart: on-failure - build: - context: ../Console.Logging.Microsoft - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.Logging.Microsoft/.dockerignore b/Console.Logging.Microsoft/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Logging.Microsoft/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Logging.Microsoft/.github/config/slack.yml b/Console.Logging.Microsoft/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Logging.Microsoft/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml b/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 7f53a173..00000000 --- a/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Logging.Microsoft - IMAGE_NAME: console.logging.microsoft - SERVICE_NAME: console-logging-microsoft - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Logging.Microsoft/.gitignore b/Console.Logging.Microsoft/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Logging.Microsoft/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Logging.Microsoft/.kubernetes/configmap.yaml b/Console.Logging.Microsoft/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Logging.Microsoft/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Logging.Microsoft/.kubernetes/cronjob.yaml b/Console.Logging.Microsoft/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.Logging.Microsoft/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Properties/DoNotParallelize.cs b/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Tests.Console.Logging.Microsoft.csproj b/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Tests.Console.Logging.Microsoft.csproj deleted file mode 100644 index 15c8c082..00000000 --- a/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Tests.Console.Logging.Microsoft.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft.sln b/Console.Logging.Microsoft/Console.Logging.Microsoft.sln deleted file mode 100644 index 42f29852..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Logging.Microsoft", "Console.Logging.Microsoft\Console.Logging.Microsoft.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Logging.Microsoft", ".tests\Tests.Console.Logging.Microsoft\Tests.Console.Logging.Microsoft.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Microsoft", "..\..\Nano.Library\Nano.Logging.Microsoft\Nano.Logging.Microsoft.csproj", "{EF1890AC-F965-E660-35B6-4E181BEB3ED8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.Build.0 = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {EF1890AC-F965-E660-35B6-4E181BEB3ED8} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {68215D72-880F-683C-BFEE-ED19A425A5F2} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Console.Logging.Microsoft.csproj b/Console.Logging.Microsoft/Console.Logging.Microsoft/Console.Logging.Microsoft.csproj deleted file mode 100644 index ae5f47a1..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/Console.Logging.Microsoft.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Dockerfile.Local b/Console.Logging.Microsoft/Console.Logging.Microsoft/Dockerfile.Local deleted file mode 100644 index 2d0c8eaa..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Program.cs b/Console.Logging.Microsoft/Console.Logging.Microsoft/Program.cs deleted file mode 100644 index 7643b9eb..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Console; -using Nano.Logging.Extensions; -using Nano.Logging.Microsoft; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoLogging(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Properties/InternalsVisibleTo.cs b/Console.Logging.Microsoft/Console.Logging.Microsoft/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 09d934ee..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Logging.Microsoft")] \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Workers/ExampleWorker.cs b/Console.Logging.Microsoft/Console.Logging.Microsoft/Workers/ExampleWorker.cs deleted file mode 100644 index 0916532a..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/Workers/ExampleWorker.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; - -namespace Console.Logging.Microsoft.Workers; - -/// -/// Example Worker. -/// -/// The . -public class ExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.Delay(500, cancellationToken); - - this.logger - .LogWarning("warning from worker"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Development.json b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Production.json b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Staging.json b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.json b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.json deleted file mode 100644 index 251db522..00000000 --- a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] - } -} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Dockerfile b/Console.Logging.Microsoft/Dockerfile deleted file mode 100644 index d3277f49..00000000 --- a/Console.Logging.Microsoft/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Console.Logging.Microsoft/README.md b/Console.Logging.Microsoft/README.md deleted file mode 100644 index 4eb5a2a4..00000000 --- a/Console.Logging.Microsoft/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Console.Logging.Microsoft - -> _Nano Console application with Microsoft logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates logging with Microsoft for a console application. - -Run the application and observe how `ExampleWorker` logs a warning to the console. Also note the `LogLevelOverrides` configuration, -where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. - -> 📖 Learn more about **[Nano.Logging.Microsoft](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Microsoft)**. - -## Registration -The following logging has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoLogging(); -}) -... -``` - -## Configuration -Configured the application with the necessary logging setup. - -```json -"Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] -} -``` \ No newline at end of file diff --git a/Console.Logging.NLog/.docker/docker-compose.dcproj b/Console.Logging.NLog/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Logging.NLog/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Logging.NLog/.docker/docker-compose.yml b/Console.Logging.NLog/.docker/docker-compose.yml deleted file mode 100644 index aeb82911..00000000 --- a/Console.Logging.NLog/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.logging.nlog: - image: console.logging.nlog - restart: on-failure - build: - context: ../Console.Logging.NLog - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.Logging.NLog/.dockerignore b/Console.Logging.NLog/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Logging.NLog/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Logging.NLog/.github/config/slack.yml b/Console.Logging.NLog/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Logging.NLog/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Logging.NLog/.github/workflows/build-and-deploy.yml b/Console.Logging.NLog/.github/workflows/build-and-deploy.yml deleted file mode 100644 index e171ed89..00000000 --- a/Console.Logging.NLog/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Logging.NLog - IMAGE_NAME: console.logging.nlog - SERVICE_NAME: console-logging-nlog - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Logging.NLog/.gitignore b/Console.Logging.NLog/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Logging.NLog/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Logging.NLog/.kubernetes/configmap.yaml b/Console.Logging.NLog/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Logging.NLog/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Logging.NLog/.kubernetes/cronjob.yaml b/Console.Logging.NLog/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.Logging.NLog/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Properties/DoNotParallelize.cs b/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Tests.Console.Logging.NLog.csproj b/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Tests.Console.Logging.NLog.csproj deleted file mode 100644 index d2958fd9..00000000 --- a/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Tests.Console.Logging.NLog.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Logging.NLog/Console.Logging.NLog.sln b/Console.Logging.NLog/Console.Logging.NLog.sln deleted file mode 100644 index 04ef1b18..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Logging.NLog", "Console.Logging.NLog\Console.Logging.NLog.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Logging.NLog", ".tests\Tests.Console.Logging.NLog\Tests.Console.Logging.NLog.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.NLog", "..\..\Nano.Library\Nano.Logging.NLog\Nano.Logging.NLog.csproj", "{EF1890AC-F965-E660-35B6-4E181BEB3ED8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.Build.0 = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {EF1890AC-F965-E660-35B6-4E181BEB3ED8} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {68215D72-880F-683C-BFEE-ED19A425A5F2} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Logging.NLog/Console.Logging.NLog/Console.Logging.NLog.csproj b/Console.Logging.NLog/Console.Logging.NLog/Console.Logging.NLog.csproj deleted file mode 100644 index 8fafd5d3..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/Console.Logging.NLog.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/Dockerfile.Local b/Console.Logging.NLog/Console.Logging.NLog/Dockerfile.Local deleted file mode 100644 index 26d2db27..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Logging.NLog.dll"] \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/Program.cs b/Console.Logging.NLog/Console.Logging.NLog/Program.cs deleted file mode 100644 index 00862263..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Console; -using Nano.Logging.Extensions; -using Nano.Logging.NLog; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoLogging(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/Properties/InternalsVisibleTo.cs b/Console.Logging.NLog/Console.Logging.NLog/Properties/InternalsVisibleTo.cs deleted file mode 100644 index f8803c57..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Logging.NLog")] \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/Workers/ExampleWorker.cs b/Console.Logging.NLog/Console.Logging.NLog/Workers/ExampleWorker.cs deleted file mode 100644 index 185dfecb..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/Workers/ExampleWorker.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; - -namespace Console.Logging.NLog.Workers; - -/// -/// Example Worker. -/// -/// The . -public class ExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.Delay(500, cancellationToken); - - this.logger - .LogWarning("warning from worker"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Development.json b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Production.json b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Staging.json b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/appsettings.json b/Console.Logging.NLog/Console.Logging.NLog/appsettings.json deleted file mode 100644 index 251db522..00000000 --- a/Console.Logging.NLog/Console.Logging.NLog/appsettings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] - } -} \ No newline at end of file diff --git a/Console.Logging.NLog/Dockerfile b/Console.Logging.NLog/Dockerfile deleted file mode 100644 index 8eac5ef1..00000000 --- a/Console.Logging.NLog/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Logging.NLog.dll"] \ No newline at end of file diff --git a/Console.Logging.NLog/README.md b/Console.Logging.NLog/README.md deleted file mode 100644 index 5bce3ceb..00000000 --- a/Console.Logging.NLog/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Console.Logging.NLog - -> _Nano Console application with NLog logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates logging with NLog for a console application. - -Run the application and observe how `ExampleWorker` logs a warning to the console. Also note the `LogLevelOverrides` configuration, -where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. - -> 📖 Learn more about **[Nano.Logging.NLog](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.NLog)**. - -## Registration -The following logging has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoLogging(); -}) -... -``` - -## Configuration -Configured the application with the necessary logging setup. - -```json -"Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] -} -``` diff --git a/Console.Logging.Serilog/.docker/docker-compose.dcproj b/Console.Logging.Serilog/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Logging.Serilog/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Logging.Serilog/.docker/docker-compose.yml b/Console.Logging.Serilog/.docker/docker-compose.yml deleted file mode 100644 index b28a3fb6..00000000 --- a/Console.Logging.Serilog/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.logging.serilog: - image: console.logging.serilog - restart: on-failure - build: - context: ../Console.Logging.Serilog - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.Logging.Serilog/.dockerignore b/Console.Logging.Serilog/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Logging.Serilog/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Logging.Serilog/.github/config/slack.yml b/Console.Logging.Serilog/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Logging.Serilog/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml b/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 7930e968..00000000 --- a/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Logging.Serilog - IMAGE_NAME: console.logging.serilog - SERVICE_NAME: console-logging-serilog - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Logging.Serilog/.gitignore b/Console.Logging.Serilog/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Logging.Serilog/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Logging.Serilog/.kubernetes/configmap.yaml b/Console.Logging.Serilog/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Logging.Serilog/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Logging.Serilog/.kubernetes/cronjob.yaml b/Console.Logging.Serilog/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.Logging.Serilog/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Properties/DoNotParallelize.cs b/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Tests.Console.Logging.Serilog.csproj b/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Tests.Console.Logging.Serilog.csproj deleted file mode 100644 index b7c58339..00000000 --- a/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Tests.Console.Logging.Serilog.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Logging.Serilog/Console.Logging.Serilog.sln b/Console.Logging.Serilog/Console.Logging.Serilog.sln deleted file mode 100644 index 19f00bf2..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Logging.Serilog", "Console.Logging.Serilog\Console.Logging.Serilog.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Logging.Serilog", ".tests\Tests.Console.Logging.Serilog\Tests.Console.Logging.Serilog.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Serilog", "..\..\Nano.Library\Nano.Logging.Serilog\Nano.Logging.Serilog.csproj", "{EF1890AC-F965-E660-35B6-4E181BEB3ED8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.Build.0 = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {EF1890AC-F965-E660-35B6-4E181BEB3ED8} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {68215D72-880F-683C-BFEE-ED19A425A5F2} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Console.Logging.Serilog.csproj b/Console.Logging.Serilog/Console.Logging.Serilog/Console.Logging.Serilog.csproj deleted file mode 100644 index 903628f2..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/Console.Logging.Serilog.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Dockerfile.Local b/Console.Logging.Serilog/Console.Logging.Serilog/Dockerfile.Local deleted file mode 100644 index 8f3057fd..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Program.cs b/Console.Logging.Serilog/Console.Logging.Serilog/Program.cs deleted file mode 100644 index 69e4e373..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Console; -using Nano.Logging.Extensions; -using Nano.Logging.Serilog; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoLogging(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Properties/InternalsVisibleTo.cs b/Console.Logging.Serilog/Console.Logging.Serilog/Properties/InternalsVisibleTo.cs deleted file mode 100644 index bb321253..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Logging.Serilog")] \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Workers/ExampleWorker.cs b/Console.Logging.Serilog/Console.Logging.Serilog/Workers/ExampleWorker.cs deleted file mode 100644 index 5bf16abe..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/Workers/ExampleWorker.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; - -namespace Console.Logging.Serilog.Workers; - -/// -/// Example Worker. -/// -/// The . -public class ExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.Delay(500, cancellationToken); - - this.logger - .LogWarning("warning from worker"); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Development.json b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Production.json b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Staging.json b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.json b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.json deleted file mode 100644 index 251db522..00000000 --- a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] - } -} \ No newline at end of file diff --git a/Console.Logging.Serilog/Dockerfile b/Console.Logging.Serilog/Dockerfile deleted file mode 100644 index f7e09b43..00000000 --- a/Console.Logging.Serilog/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Console.Logging.Serilog/README.md b/Console.Logging.Serilog/README.md deleted file mode 100644 index a79aadc9..00000000 --- a/Console.Logging.Serilog/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# Console.Logging.Serilog - -> _Nano Console application with Serilog logging._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates logging with Serilog for a console application. - -Run the application and observe how `ExampleWorker` logs a warning to the console. Also note the `LogLevelOverrides` configuration, -where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. - -> 📖 Learn more about **[Nano.Logging.Serilog](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Serilog)**. - -## Registration -The following logging has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(services => -{ - services - .AddNanoLogging(); -}) -... -``` - -## Configuration -Configured the application with the necessary logging setup. - -```json -"Logging": { - "LogLevel": "Information", - "LogLevelOverrides": [ - { - "Namespace": "Microsoft", - "LogLevel": "Warning" - } - ] -} -``` diff --git a/Console.StartupTasks/.docker/docker-compose.dcproj b/Console.StartupTasks/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.StartupTasks/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.StartupTasks/.docker/docker-compose.yml b/Console.StartupTasks/.docker/docker-compose.yml deleted file mode 100644 index b1212011..00000000 --- a/Console.StartupTasks/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.startuptasks: - image: console.startuptasks - restart: on-failure - build: - context: ../Console.StartupTasks - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.StartupTasks/.dockerignore b/Console.StartupTasks/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.StartupTasks/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.StartupTasks/.github/config/slack.yml b/Console.StartupTasks/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.StartupTasks/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.StartupTasks/.github/workflows/build-and-deploy.yml b/Console.StartupTasks/.github/workflows/build-and-deploy.yml deleted file mode 100644 index df9d5e96..00000000 --- a/Console.StartupTasks/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.StartupTasks - IMAGE_NAME: console.startuptasks - SERVICE_NAME: console-startuptasks - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.StartupTasks/.gitignore b/Console.StartupTasks/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.StartupTasks/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.StartupTasks/.kubernetes/configmap.yaml b/Console.StartupTasks/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.StartupTasks/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.StartupTasks/.kubernetes/cronjob.yaml b/Console.StartupTasks/.kubernetes/cronjob.yaml deleted file mode 100644 index 64a6dce3..00000000 --- a/Console.StartupTasks/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Properties/DoNotParallelize.cs b/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Tests.Console.StartupTasks.csproj b/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Tests.Console.StartupTasks.csproj deleted file mode 100644 index c78ddc11..00000000 --- a/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Tests.Console.StartupTasks.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.StartupTasks/Console.StartupTasks.sln b/Console.StartupTasks/Console.StartupTasks.sln deleted file mode 100644 index 80c9db04..00000000 --- a/Console.StartupTasks/Console.StartupTasks.sln +++ /dev/null @@ -1,123 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 d18.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.StartupTasks", "Console.StartupTasks\Console.StartupTasks.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.StartupTasks", ".tests\Tests.Console.StartupTasks\Tests.Console.StartupTasks.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.StartupTasks/Console.StartupTasks/Console.StartupTasks.csproj b/Console.StartupTasks/Console.StartupTasks/Console.StartupTasks.csproj deleted file mode 100644 index e0dfc0c1..00000000 --- a/Console.StartupTasks/Console.StartupTasks/Console.StartupTasks.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Dockerfile.Local b/Console.StartupTasks/Console.StartupTasks/Dockerfile.Local deleted file mode 100644 index afbd78eb..00000000 --- a/Console.StartupTasks/Console.StartupTasks/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.StartupTasks.dll"] \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Program.cs b/Console.StartupTasks/Console.StartupTasks/Program.cs deleted file mode 100644 index 1aa684bd..00000000 --- a/Console.StartupTasks/Console.StartupTasks/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Console; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Properties/InternalsVisibleTo.cs b/Console.StartupTasks/Console.StartupTasks/Properties/InternalsVisibleTo.cs deleted file mode 100644 index d6deb4b6..00000000 --- a/Console.StartupTasks/Console.StartupTasks/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.StartupTasks")] \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Startup/ExampleStartupTask.cs b/Console.StartupTasks/Console.StartupTasks/Startup/ExampleStartupTask.cs deleted file mode 100644 index bbde7b51..00000000 --- a/Console.StartupTasks/Console.StartupTasks/Startup/ExampleStartupTask.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Startup; - -namespace Console.StartupTasks.Startup; - -/// -/// Example Startup Task. -/// -/// The . -public class ExampleStartupTask(ILogger logger) : BaseStartupTask(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Startup Task Started..."); - - await Task.Delay(2000, cancellationToken); - - System.Console.WriteLine(DateTime.UtcNow); - - System.Console.WriteLine("Example Startup Task Completed..."); - } -} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Workers/ExampleWorker.cs b/Console.StartupTasks/Console.StartupTasks/Workers/ExampleWorker.cs deleted file mode 100644 index 4c0633f7..00000000 --- a/Console.StartupTasks/Console.StartupTasks/Workers/ExampleWorker.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Console.StartupTasks.Workers; - -/// -/// Example Worker. -/// -/// The . -public class ExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.Delay(100, cancellationToken); - - System.Console.WriteLine(DateTime.UtcNow); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/appsettings.Development.json b/Console.StartupTasks/Console.StartupTasks/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.StartupTasks/Console.StartupTasks/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/appsettings.Production.json b/Console.StartupTasks/Console.StartupTasks/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.StartupTasks/Console.StartupTasks/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/appsettings.Staging.json b/Console.StartupTasks/Console.StartupTasks/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.StartupTasks/Console.StartupTasks/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/appsettings.json b/Console.StartupTasks/Console.StartupTasks/appsettings.json deleted file mode 100644 index c7b251f2..00000000 --- a/Console.StartupTasks/Console.StartupTasks/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - } -} \ No newline at end of file diff --git a/Console.StartupTasks/Dockerfile b/Console.StartupTasks/Dockerfile deleted file mode 100644 index c0ce3143..00000000 --- a/Console.StartupTasks/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.StartupTasks.dll"] \ No newline at end of file diff --git a/Console.StartupTasks/README.md b/Console.StartupTasks/README.md deleted file mode 100644 index 47cef7a8..00000000 --- a/Console.StartupTasks/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Console.StartupTasks - -> _Nano Console application with startup tasks._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates a waiting worker that delays its execution until all registered startup tasks have completed, -ensuring that the console application only runs after initialization is finished. - -Run the application and observe how both example workers wait for the startup tasks to complete before executing their logic. - -> 📖 Learn more about **[Nano Startup Tasks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App#startup-tasks)**. diff --git a/Console.Storage.Azure/.docker/docker-compose.dcproj b/Console.Storage.Azure/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Storage.Azure/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Storage.Azure/.docker/docker-compose.yml b/Console.Storage.Azure/.docker/docker-compose.yml deleted file mode 100644 index 0750c797..00000000 --- a/Console.Storage.Azure/.docker/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -services: - console.storage.azure: - image: console.storage.azure - restart: on-failure - build: - context: ../Console.Storage.Azure - dockerfile: "Dockerfile.Local" - networks: - - network - volumes: - - ./bin/nano-storage-azure:/mnt/nano-storage-azure - -networks: - network: - name: network - external: true diff --git a/Console.Storage.Azure/.dockerignore b/Console.Storage.Azure/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Storage.Azure/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Storage.Azure/.github/config/slack.yml b/Console.Storage.Azure/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Storage.Azure/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Storage.Azure/.github/workflows/build-and-deploy.yml b/Console.Storage.Azure/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 45dd4072..00000000 --- a/Console.Storage.Azure/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,160 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Storage.Azure - IMAGE_NAME: console.storage.azure - SERVICE_NAME: console-storage-azure - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - STORAGE_SHARE_NAME: nano-storage-azure - STORAGE_CREDENTIALS_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_STORAGE_CREDENTIALS_ID || secrets.STAGING_STORAGE_CREDENTIALS_ID }} - STORAGE_CREDENTIALS_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_STORAGE_CREDENTIALS_SECRET || secrets.STAGING_STORAGE_CREDENTIALS_SECRET }} - STORAGE_SIZE: 1000 - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Create Fileshare - shell: pwsh - run: | - $env:EXISTING_FILE_SHARE = sudo az storage share list --account-name $env:STORAGE_CREDENTIALS_ID --account-key $env:STORAGE_CREDENTIALS_SECRET --query "[?contains(name, '$env:STORAGE_SHARE_NAME')].[name]" -o tsv; - if ([string]::IsNullOrEmpty($env:EXISTING_FILE_SHARE)) - { - sudo az storage share create -n $env:STORAGE_SHARE_NAME --account-name $env:STORAGE_CREDENTIALS_ID --account-key $env:STORAGE_CREDENTIALS_SECRET --quota $env:STORAGE_SIZE; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - } - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Storage.Azure/.gitignore b/Console.Storage.Azure/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Storage.Azure/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Storage.Azure/.kubernetes/configmap.yaml b/Console.Storage.Azure/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Storage.Azure/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Storage.Azure/.kubernetes/cronjob.yaml b/Console.Storage.Azure/.kubernetes/cronjob.yaml deleted file mode 100644 index 03581cdf..00000000 --- a/Console.Storage.Azure/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,76 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - env: - - name: Storage__Credentials__Id - valueFrom: - secretKeyRef: - name: storage-account-secret - key: azurestorageaccountname - - name: Storage__Credentials__Secret - valueFrom: - secretKeyRef: - name: storage-account-secret - key: azurestorageaccountkey - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - - name: tmp - mountPath: /tmp - restartPolicy: OnFailure - volumes: - - name: %SERVICE_NAME%-volume - azureFile: - secretName: storage-account-secret - shareName: %STORAGE_SHARE_NAME% - readOnly: false - - name: tmp - emptyDir: {} - imagePullSecrets: - - name: ghcr-pull-secret - - diff --git a/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Properties/DoNotParallelize.cs b/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Tests.Console.Storage.Azure.csproj b/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Tests.Console.Storage.Azure.csproj deleted file mode 100644 index bf29923e..00000000 --- a/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Tests.Console.Storage.Azure.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Storage.Azure/Console.Storage.Azure.sln b/Console.Storage.Azure/Console.Storage.Azure.sln deleted file mode 100644 index a75c97a6..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure.sln +++ /dev/null @@ -1,137 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Storage.Azure", "Console.Storage.Azure\Console.Storage.Azure.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Storage.Azure", ".tests\Tests.Console.Storage.Azure\Tests.Console.Storage.Azure.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage", "..\..\Nano.Library\Nano.Storage\Nano.Storage.csproj", "{24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Azure", "..\..\Nano.Library\Nano.Storage.Azure\Nano.Storage.Azure.csproj", "{5DD1307B-16FA-5171-54CA-4F37F2638022}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.Build.0 = Release|Any CPU - {5DD1307B-16FA-5171-54CA-4F37F2638022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5DD1307B-16FA-5171-54CA-4F37F2638022}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5DD1307B-16FA-5171-54CA-4F37F2638022}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5DD1307B-16FA-5171-54CA-4F37F2638022}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {5DD1307B-16FA-5171-54CA-4F37F2638022} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Storage.Azure/Console.Storage.Azure/Console.Storage.Azure.csproj b/Console.Storage.Azure/Console.Storage.Azure/Console.Storage.Azure.csproj deleted file mode 100644 index 604c83ea..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/Console.Storage.Azure.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/Dockerfile.Local b/Console.Storage.Azure/Console.Storage.Azure/Dockerfile.Local deleted file mode 100644 index b4edc380..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Storage.Azure.dll"] \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/Program.cs b/Console.Storage.Azure/Console.Storage.Azure/Program.cs deleted file mode 100644 index f1a5f6c7..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Console; -using Nano.Storage.Azure; -using Nano.Storage.Extensions; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoStorage(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/Properties/InternalsVisibleTo.cs b/Console.Storage.Azure/Console.Storage.Azure/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 7ec66a84..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Storage.Azure")] \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/Workers/ExampleWorker.cs b/Console.Storage.Azure/Console.Storage.Azure/Workers/ExampleWorker.cs deleted file mode 100644 index cf04dcb5..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/Workers/ExampleWorker.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.IO; -using System.Text; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using Nano.Storage.Abstractions; -using System.Threading; -using System.Threading.Tasks; - -namespace Console.Storage.Azure.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IPathProvider pathProvider) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - var fileName = Path.GetFileName("test-file.txt"); - var savePath = Path.Combine(pathProvider.Root, fileName); - - await File.WriteAllTextAsync(savePath, "content", Encoding.UTF8, cancellationToken); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Development.json b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Production.json b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Staging.json b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/appsettings.json b/Console.Storage.Azure/Console.Storage.Azure/appsettings.json deleted file mode 100644 index 85d28d81..00000000 --- a/Console.Storage.Azure/Console.Storage.Azure/appsettings.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Storage": { - "ShareName": "nano-storage-azure", - "Credentials": { - "Id": null, - "Secret": null - } - } -} \ No newline at end of file diff --git a/Console.Storage.Azure/Dockerfile b/Console.Storage.Azure/Dockerfile deleted file mode 100644 index 3d7996cd..00000000 --- a/Console.Storage.Azure/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Storage.Azure.dll"] \ No newline at end of file diff --git a/Console.Storage.Azure/README.md b/Console.Storage.Azure/README.md deleted file mode 100644 index 769c24ae..00000000 --- a/Console.Storage.Azure/README.md +++ /dev/null @@ -1,124 +0,0 @@ -# Console.Storage.Azure - -> _Nano Console application with azure storage._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates creating a file and saving it to a mapped file share. -When running locally, files are **NOT** written to the Azure File Share. Instead, Docker mounts a local directory to simulate the file share. -Files are saved in `.docker/bin/`. - -> 📖 Learn more about **[Nano.Storage.Azure](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Storage.Azure)**. - -## Registration -The following storage has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(x => -{ - x.AddNanoStorage(); -}) -... -``` - -## Configuration -Configured the application with the necessary storage setup. - -```json -"Storage": { - "ShareName": "nano-storage-azure", - "Credentials": { - "Id": "id", - "Secret": "secret" - } -} -``` - -## Docker Compose -Mapped the fileshare in `docker-compose.yml`. - -```yaml -docker - volumes: - - ./bin/nano-storage-azure:/mnt/nano-storage-azure -``` - -## Kubernetes -Added the volumes, volume mounts and secrets to the `cronjob.yaml`. - -```json -spec: - template: - spec: - containers: - env: - - name: Storage__Credentials__Id - valueFrom: - secretKeyRef: - name: storage-account-secret - key: azurestorageaccountname - - name: Storage__Credentials__Secret - valueFrom: - secretKeyRef: - name: storage-account-secret - key: azurestorageaccountkey - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - volumes: - - name: tmp - emptyDir: {} - - name: %SERVICE_NAME%-volume - azureFile: - secretName: storage-account-secret - shareName: %STORAGE_SHARE_NAME% - readOnly: false -``` - -## GitHub Actions -Add the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - STORAGE_SHARE_NAME: nano-storage-azure - STORAGE_CREDENTIALS_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_STORAGE_CREDENTIALS_ID || secrets.STAGING_STORAGE_CREDENTIALS_ID }} - STORAGE_CREDENTIALS_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_STORAGE_CREDENTIALS_SECRET || secrets.STAGING_STORAGE_CREDENTIALS_SECRET }} - STORAGE_SIZE: 1000 -``` - -Additionally, this step has been added to ensure the file share is created before the application is deployed. - -```yaml -- name: Create Fileshare - shell: pwsh - run: | - $env:EXISTING_FILE_SHARE = sudo az storage share list --account-name $env:STORAGE_CREDENTIALS_ID --account-key $env:STORAGE_CREDENTIALS_SECRET --query "[?contains(name, '$env:STORAGE_SHARE_NAME')].[name]" -o tsv; - if ([string]::IsNullOrEmpty($env:EXISTING_FILE_SHARE)) - { - sudo az storage share create -n $env:STORAGE_SHARE_NAME --account-name $env:STORAGE_CREDENTIALS_ID --account-key $env:STORAGE_CREDENTIALS_SECRET --quota $env:STORAGE_SIZE; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - } -``` \ No newline at end of file diff --git a/Console.Storage.Local/.docker/docker-compose.dcproj b/Console.Storage.Local/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Storage.Local/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Storage.Local/.docker/docker-compose.yml b/Console.Storage.Local/.docker/docker-compose.yml deleted file mode 100644 index f877ac93..00000000 --- a/Console.Storage.Local/.docker/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -services: - console.storage.local: - image: console.storage.local - restart: on-failure - build: - context: ../Console.Storage.Local - dockerfile: "Dockerfile.Local" - networks: - - network - volumes: - - ./bin/nano-storage-local:/mnt/nano-storage-local - -networks: - network: - name: network - external: true diff --git a/Console.Storage.Local/.dockerignore b/Console.Storage.Local/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Storage.Local/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Storage.Local/.github/config/slack.yml b/Console.Storage.Local/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Storage.Local/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Storage.Local/.github/workflows/build-and-deploy.yml b/Console.Storage.Local/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 6fcaacb3..00000000 --- a/Console.Storage.Local/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,158 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Storage.Local - IMAGE_NAME: console.storage.local - SERVICE_NAME: console-storage-local - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - STORAGE_SHARE_NAME: nano-storage-local - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/storage-storageclass.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-storageclass.tmp.yaml; - sudo kubectl apply -f .kubernetes/storage-storageclass.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/storage-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pvc.tmp.yaml; - sudo kubectl apply -f .kubernetes/storage-pvc.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Storage.Local/.gitignore b/Console.Storage.Local/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Storage.Local/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Storage.Local/.kubernetes/configmap.yaml b/Console.Storage.Local/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Storage.Local/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Storage.Local/.kubernetes/cronjob.yaml b/Console.Storage.Local/.kubernetes/cronjob.yaml deleted file mode 100644 index a2a45d83..00000000 --- a/Console.Storage.Local/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,62 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - - name: tmp - mountPath: /tmp - restartPolicy: OnFailure - volumes: - - name: %STORAGE_SHARE_NAME% - emptyDir: {} - - name: tmp - emptyDir: {} - imagePullSecrets: - - name: ghcr-pull-secret - - diff --git a/Console.Storage.Local/.kubernetes/storage-pvc.yaml b/Console.Storage.Local/.kubernetes/storage-pvc.yaml deleted file mode 100644 index 2285607e..00000000 --- a/Console.Storage.Local/.kubernetes/storage-pvc.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: %SERVICE_NAME%-pvc -spec: - accessModes: - - ReadWriteOnce - storageClassName: %SERVICE_NAME%-storage-class - resources: - requests: - storage: %STORAGE_SIZE% \ No newline at end of file diff --git a/Console.Storage.Local/.kubernetes/storage-storageclass.yaml b/Console.Storage.Local/.kubernetes/storage-storageclass.yaml deleted file mode 100644 index f9ccaa3e..00000000 --- a/Console.Storage.Local/.kubernetes/storage-storageclass.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: %SERVICE_NAME%-storage-class -provisioner: kubernetes.io/azure-disk -parameters: - storageaccounttype: Standard_LRS - kind: Managed -reclaimPolicy: Retain -volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Properties/DoNotParallelize.cs b/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Tests.Console.Storage.Local.csproj b/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Tests.Console.Storage.Local.csproj deleted file mode 100644 index e03ab3b2..00000000 --- a/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Tests.Console.Storage.Local.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Storage.Local/Console.Storage.Local.sln b/Console.Storage.Local/Console.Storage.Local.sln deleted file mode 100644 index 53476dc1..00000000 --- a/Console.Storage.Local/Console.Storage.Local.sln +++ /dev/null @@ -1,140 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Storage.Local", "Console.Storage.Local\Console.Storage.Local.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - .kubernetes\pvc.yaml = .kubernetes\pvc.yaml - .kubernetes\storage-pvc.yaml = .kubernetes\storage-pvc.yaml - .kubernetes\storage-storageclass.yaml = .kubernetes\storage-storageclass.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Storage.Local", ".tests\Tests.Console.Storage.Local\Tests.Console.Storage.Local.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage", "..\..\Nano.Library\Nano.Storage\Nano.Storage.csproj", "{24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Local", "..\..\Nano.Library\Nano.Storage.Local\Nano.Storage.Local.csproj", "{6D21CF50-D3ED-4D55-27D9-D426E661A5E4}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.Build.0 = Release|Any CPU - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6D21CF50-D3ED-4D55-27D9-D426E661A5E4} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Storage.Local/Console.Storage.Local/Console.Storage.Local.csproj b/Console.Storage.Local/Console.Storage.Local/Console.Storage.Local.csproj deleted file mode 100644 index e3e35dff..00000000 --- a/Console.Storage.Local/Console.Storage.Local/Console.Storage.Local.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/Dockerfile.Local b/Console.Storage.Local/Console.Storage.Local/Dockerfile.Local deleted file mode 100644 index 1839f37a..00000000 --- a/Console.Storage.Local/Console.Storage.Local/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Storage.Local.dll"] \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/Program.cs b/Console.Storage.Local/Console.Storage.Local/Program.cs deleted file mode 100644 index 04d69c99..00000000 --- a/Console.Storage.Local/Console.Storage.Local/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Nano.App.Console; -using Nano.Storage.Extensions; -using Nano.Storage.Local; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - x.AddNanoStorage(); - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/Properties/InternalsVisibleTo.cs b/Console.Storage.Local/Console.Storage.Local/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 96941052..00000000 --- a/Console.Storage.Local/Console.Storage.Local/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Storage.Local")] \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/Workers/ExampleWorker.cs b/Console.Storage.Local/Console.Storage.Local/Workers/ExampleWorker.cs deleted file mode 100644 index c7e62b7f..00000000 --- a/Console.Storage.Local/Console.Storage.Local/Workers/ExampleWorker.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.IO; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Nano.Storage.Abstractions; - -namespace Console.Storage.Local.Workers; - -/// -/// Example Worker. -/// -/// The . -/// The . -public class ExampleWorker(ILogger logger, IPathProvider pathProvider) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - var fileName = Path.GetFileName("test-file.txt"); - var savePath = Path.Combine(pathProvider.Root, fileName); - - await File.WriteAllTextAsync(savePath, "content", Encoding.UTF8, cancellationToken); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/appsettings.Development.json b/Console.Storage.Local/Console.Storage.Local/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Storage.Local/Console.Storage.Local/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/appsettings.Production.json b/Console.Storage.Local/Console.Storage.Local/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Storage.Local/Console.Storage.Local/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/appsettings.Staging.json b/Console.Storage.Local/Console.Storage.Local/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Storage.Local/Console.Storage.Local/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/appsettings.json b/Console.Storage.Local/Console.Storage.Local/appsettings.json deleted file mode 100644 index 7bce4a53..00000000 --- a/Console.Storage.Local/Console.Storage.Local/appsettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - }, - "Storage": { - "ShareName": "nano-storage-local" - } -} \ No newline at end of file diff --git a/Console.Storage.Local/Dockerfile b/Console.Storage.Local/Dockerfile deleted file mode 100644 index 5e4b05e8..00000000 --- a/Console.Storage.Local/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Storage.Local.dll"] \ No newline at end of file diff --git a/Console.Storage.Local/README.md b/Console.Storage.Local/README.md deleted file mode 100644 index 5ced1ea7..00000000 --- a/Console.Storage.Local/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# Console.Storage.Local - -> _Nano Console application with local storage._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) -* [Registration](#registration) -* [Configuration](#configuration) -* [Docker-compose](#docker-compose) -* [Kubernetes](#kubernetes) -* [GitHub Actions](#github-actions) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates creating a file and saving it to a locally mapped file-share. -Files are saved in `.docker/bin/`. - -> 📖 Learn more about **[Nano.Storage.Local](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Storage.Local)**. - -## Registration -The following storage has been registered using `ConfigureServices(...)` in `program.cs`. - -```csharp -... -.ConfigureServices(x => -{ - x.AddNanoStorage(); -}) -... -``` - -## Configuration -Configured the application with the necessary storage setup. - -```json -"Storage": { - "ShareName": "nano-storage-local" -} -``` - -## Docker Compose -Mapped the fileshare in `docker-compose.yml`. - -```yaml -docker - volumes: - - ./bin/nano-storage-local:/mnt/nano-storage-local -``` - -## Kubernetes -Added two additional kubernetes templates, `storage-storageclass.yaml` and `storage-pvc.yaml`, for dynamically manage and creating the local fileshare. - -Also, updated `cronjob.yaml` adding the volumes and volume mounts. - -```json -spec: - template: - spec: - containers: - volumeMounts: - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - - name: tmp - mountPath: /tmp - volumes: - - name: %SERVICE_NAME%-volume - persistentVolumeClaim: - claimName: %SERVICE_NAME%-pvc - - name: tmp - emptyDir: {} -``` - -## GitHub Actions -Added the following environment variables to the `buid-and-deply.yml`. - -```yaml -env: - STORAGE_SHARE_NAME: nano-storage-local - STORAGE_SIZE: 1000 -``` - -Deployment commands have also been updated to apply each of the new Kubernetes templates. - -```powershell -Get-Content .kubernetes/{resource-name}.yaml ` - | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` - | Set-Content .kubernetes/{resource-name}.tmp.yaml; - -sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; -``` diff --git a/Console.Workers/.docker/docker-compose.dcproj b/Console.Workers/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console.Workers/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console.Workers/.docker/docker-compose.yml b/Console.Workers/.docker/docker-compose.yml deleted file mode 100644 index 4c2e3672..00000000 --- a/Console.Workers/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.workers: - image: console.workers - restart: on-failure - build: - context: ../Console.Workers - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console.Workers/.dockerignore b/Console.Workers/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console.Workers/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console.Workers/.github/config/slack.yml b/Console.Workers/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console.Workers/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Workers/.github/workflows/build-and-deploy.yml b/Console.Workers/.github/workflows/build-and-deploy.yml deleted file mode 100644 index bca9f1a9..00000000 --- a/Console.Workers/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Workers - IMAGE_NAME: console.workers - SERVICE_NAME: console-workers - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Workers/.gitignore b/Console.Workers/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console.Workers/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Workers/.kubernetes/configmap.yaml b/Console.Workers/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console.Workers/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Workers/.kubernetes/cronjob.yaml b/Console.Workers/.kubernetes/cronjob.yaml deleted file mode 100644 index e6c9edaa..00000000 --- a/Console.Workers/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret - - diff --git a/Console.Workers/.tests/Tests.Console.Workers/Properties/DoNotParallelize.cs b/Console.Workers/.tests/Tests.Console.Workers/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console.Workers/.tests/Tests.Console.Workers/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Workers/.tests/Tests.Console.Workers/Tests.Console.Workers.csproj b/Console.Workers/.tests/Tests.Console.Workers/Tests.Console.Workers.csproj deleted file mode 100644 index 49d1c17a..00000000 --- a/Console.Workers/.tests/Tests.Console.Workers/Tests.Console.Workers.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console.Workers/Console.Workers.sln b/Console.Workers/Console.Workers.sln deleted file mode 100644 index 39e8f574..00000000 --- a/Console.Workers/Console.Workers.sln +++ /dev/null @@ -1,123 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 d18.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Workers", "Console.Workers\Console.Workers.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Workers", ".tests\Tests.Console.Workers\Tests.Console.Workers.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console.Workers/Console.Workers/Console.Workers.csproj b/Console.Workers/Console.Workers/Console.Workers.csproj deleted file mode 100644 index e0dfc0c1..00000000 --- a/Console.Workers/Console.Workers/Console.Workers.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Dockerfile.Local b/Console.Workers/Console.Workers/Dockerfile.Local deleted file mode 100644 index 0251200b..00000000 --- a/Console.Workers/Console.Workers/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Workers.dll"] \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Program.cs b/Console.Workers/Console.Workers/Program.cs deleted file mode 100644 index 1aa684bd..00000000 --- a/Console.Workers/Console.Workers/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Console; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Properties/InternalsVisibleTo.cs b/Console.Workers/Console.Workers/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 91f2d69f..00000000 --- a/Console.Workers/Console.Workers/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Workers")] \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Workers/AnotherExampleWorker.cs b/Console.Workers/Console.Workers/Workers/AnotherExampleWorker.cs deleted file mode 100644 index 8ac7540d..00000000 --- a/Console.Workers/Console.Workers/Workers/AnotherExampleWorker.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; - -namespace Console.Workers.Workers; - -/// -/// Another Example Worker. -/// -/// The . -public class AnotherExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Another Example Worker Started..."); - - await Task.Delay(1000, cancellationToken); - - System.Console.WriteLine("Another Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Workers/ExampleWorker.cs b/Console.Workers/Console.Workers/Workers/ExampleWorker.cs deleted file mode 100644 index 06b56cbc..00000000 --- a/Console.Workers/Console.Workers/Workers/ExampleWorker.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging; -using Nano.App.Console.Workers; - -namespace Console.Workers.Workers; - -/// -/// Example Worker. -/// -/// The . -public class ExampleWorker(ILogger logger) : BaseWorker(logger) -{ - /// - /// Example On Start. - /// - /// The . - /// A . - public override async Task OnStartAsync(CancellationToken cancellationToken = default) - { - System.Console.WriteLine("Example Worker Started..."); - - await Task.Delay(500, cancellationToken); - - System.Console.WriteLine("Example Worker Completed..."); - } -} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/appsettings.Development.json b/Console.Workers/Console.Workers/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Workers/Console.Workers/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/appsettings.Production.json b/Console.Workers/Console.Workers/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Workers/Console.Workers/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/appsettings.Staging.json b/Console.Workers/Console.Workers/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console.Workers/Console.Workers/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/appsettings.json b/Console.Workers/Console.Workers/appsettings.json deleted file mode 100644 index c7b251f2..00000000 --- a/Console.Workers/Console.Workers/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - } -} \ No newline at end of file diff --git a/Console.Workers/Dockerfile b/Console.Workers/Dockerfile deleted file mode 100644 index 1a4df7b5..00000000 --- a/Console.Workers/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Workers.dll"] \ No newline at end of file diff --git a/Console.Workers/README.md b/Console.Workers/README.md deleted file mode 100644 index e207e06c..00000000 --- a/Console.Workers/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Console.Workers - -> _Nano Console application with workers._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. - -This application demonstrates multiple workers for a console application. - -Run the application and observe how both implemented workers execute, and see the console output generated by the `ExampleWorker` and `AnotherExampleWorker`. - -> 📖 Learn more about **[Nano Console Workers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Console#console-workers)**. diff --git a/Console._Blank/.docker/docker-compose.dcproj b/Console._Blank/.docker/docker-compose.dcproj deleted file mode 100644 index acca666d..00000000 --- a/Console._Blank/.docker/docker-compose.dcproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - 2.1 - Linux - 557a0c48-da6a-4d7c-8668-94f08a390f4b - false - - - Nano.Template.Console - - - - - \ No newline at end of file diff --git a/Console._Blank/.docker/docker-compose.yml b/Console._Blank/.docker/docker-compose.yml deleted file mode 100644 index de34d983..00000000 --- a/Console._Blank/.docker/docker-compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -services: - console.blank: - image: console.blank - restart: on-failure - build: - context: ../Console.Blank - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - external: true diff --git a/Console._Blank/.dockerignore b/Console._Blank/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Console._Blank/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Console._Blank/.github/config/slack.yml b/Console._Blank/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Console._Blank/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console._Blank/.github/workflows/build-and-deploy.yml b/Console._Blank/.github/workflows/build-and-deploy.yml deleted file mode 100644 index 30741d0c..00000000 --- a/Console._Blank/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,143 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Console.Blank - IMAGE_NAME: console.blank - SERVICE_NAME: console-blank - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_HISTORY_COUNT: 4 - KUBERNETES_MEMORY_REQUEST: 256Mi - KUBERNETES_MEMORY_LIMIT: 768Mi - KUBERNETES_CPU_REQUEST: 50m - KUBERNETES_CPU_LIMIT: 150m - KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" - DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; - sudo kubectl apply -f .kubernetes/cronjob.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console._Blank/.gitignore b/Console._Blank/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Console._Blank/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console._Blank/.kubernetes/configmap.yaml b/Console._Blank/.kubernetes/configmap.yaml deleted file mode 100644 index f2293b0f..00000000 --- a/Console._Blank/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console._Blank/.kubernetes/cronjob.yaml b/Console._Blank/.kubernetes/cronjob.yaml deleted file mode 100644 index 1590c0e5..00000000 --- a/Console._Blank/.kubernetes/cronjob.yaml +++ /dev/null @@ -1,50 +0,0 @@ -apiVersion: batch/v1 -kind: CronJob -metadata: - name: %SERVICE_NAME% -spec: - schedule: %KUBERNETES_CRONJOB_SCHEDULE% - concurrencyPolicy: Forbid - failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - jobTemplate: - metadata: - labels: - app: %SERVICE_NAME% - spec: - template: - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - restartPolicy: OnFailure - imagePullSecrets: - - name: ghcr-pull-secret diff --git a/Console._Blank/.tests/Tests.Console.Blank/Properties/DoNotParallelize.cs b/Console._Blank/.tests/Tests.Console.Blank/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Console._Blank/.tests/Tests.Console.Blank/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console._Blank/.tests/Tests.Console.Blank/Tests.Console.Blank.csproj b/Console._Blank/.tests/Tests.Console.Blank/Tests.Console.Blank.csproj deleted file mode 100644 index f6d022d5..00000000 --- a/Console._Blank/.tests/Tests.Console.Blank/Tests.Console.Blank.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Console._Blank/Console.Blank.sln b/Console._Blank/Console.Blank.sln deleted file mode 100644 index 77c0c608..00000000 --- a/Console._Blank/Console.Blank.sln +++ /dev/null @@ -1,123 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.2.11415.280 d18.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Blank", "Console.Blank\Console.Blank.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Blank", ".tests\Tests.Console.Blank\Tests.Console.Blank.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU - {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} - {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Console._Blank/Console.Blank/Console.Blank.csproj b/Console._Blank/Console.Blank/Console.Blank.csproj deleted file mode 100644 index e0dfc0c1..00000000 --- a/Console._Blank/Console.Blank/Console.Blank.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - - net10.0 - - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Console._Blank/Console.Blank/Dockerfile.Local b/Console._Blank/Console.Blank/Dockerfile.Local deleted file mode 100644 index 03b8a154..00000000 --- a/Console._Blank/Console.Blank/Dockerfile.Local +++ /dev/null @@ -1,5 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -ENTRYPOINT ["dotnet", "Console.Blank.dll"] \ No newline at end of file diff --git a/Console._Blank/Console.Blank/Program.cs b/Console._Blank/Console.Blank/Program.cs deleted file mode 100644 index 2a6b33aa..00000000 --- a/Console._Blank/Console.Blank/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Nano.App.Console; - -NanoConsoleApplication - .ConfigureApp(args) - .ConfigureServices(x => - { - // Blank - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Console._Blank/Console.Blank/Properties/InternalsVisibleTo.cs b/Console._Blank/Console.Blank/Properties/InternalsVisibleTo.cs deleted file mode 100644 index 9c0858cc..00000000 --- a/Console._Blank/Console.Blank/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Console.Blank")] \ No newline at end of file diff --git a/Console._Blank/Console.Blank/appsettings.Development.json b/Console._Blank/Console.Blank/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console._Blank/Console.Blank/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console._Blank/Console.Blank/appsettings.Production.json b/Console._Blank/Console.Blank/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console._Blank/Console.Blank/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console._Blank/Console.Blank/appsettings.Staging.json b/Console._Blank/Console.Blank/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Console._Blank/Console.Blank/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Console._Blank/Console.Blank/appsettings.json b/Console._Blank/Console.Blank/appsettings.json deleted file mode 100644 index c7b251f2..00000000 --- a/Console._Blank/Console.Blank/appsettings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0" - } -} \ No newline at end of file diff --git a/Console._Blank/Dockerfile b/Console._Blank/Dockerfile deleted file mode 100644 index 16d41684..00000000 --- a/Console._Blank/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENTRYPOINT ["dotnet", "Console.Blank.dll"] \ No newline at end of file diff --git a/Console._Blank/README.md b/Console._Blank/README.md deleted file mode 100644 index 79f9bd9b..00000000 --- a/Console._Blank/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Console.Blank - -> _Minimal (blank) Nano Console application._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application represents the most minimal (blank) Nano Console application setup, and has no console workers implemented. - -Its purpose is to demonstrate the required boilerplate, file structure, and general configuration needed to create a Nano Console application. -The application itself is intentionally minimal and does not do anything meaningful. Instead, it serves as a baseline from which all other Console lessons and examples -are built. - -It is recommended to review this application first to understand how a Nano Console application is generally structured and to become familiar with the -purpose of the core building blocks used in the boilerplate. - -> 📖 Learn more about **[Nano Solution Composition](https://github.com/Nano-Core/Nano.Library#solution-composition)**. diff --git a/Web._Blank/.docker/docker-compose.dcproj b/Web._Blank/.docker/docker-compose.dcproj deleted file mode 100644 index d9e8e500..00000000 --- a/Web._Blank/.docker/docker-compose.dcproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - 2.1 - Linux - false - http://localhost:{ServicePort}/docs - $(ProjectName) - - - - - \ No newline at end of file diff --git a/Web._Blank/.docker/docker-compose.yml b/Web._Blank/.docker/docker-compose.yml deleted file mode 100644 index 54765d3c..00000000 --- a/Web._Blank/.docker/docker-compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - web.blank: - image: web.blank - hostname: web-blank - restart: on-failure - ports: - - 8080:8080 - build: - context: ../Web.Blank - dockerfile: "Dockerfile.Local" - networks: - - network - -networks: - network: - name: network - driver: bridge diff --git a/Web._Blank/.dockerignore b/Web._Blank/.dockerignore deleted file mode 100644 index e694ae21..00000000 --- a/Web._Blank/.dockerignore +++ /dev/null @@ -1,12 +0,0 @@ -.dockerignore -.env -.git -.gitignore -.vs -.vscode -docker-compose.yml -docker-compose.*.yml -*/bin -*/obj -!obj/Docker/publish/* -!obj/Docker/empty/ diff --git a/Web._Blank/.github/config/slack.yml b/Web._Blank/.github/config/slack.yml deleted file mode 100644 index 3592affb..00000000 --- a/Web._Blank/.github/config/slack.yml +++ /dev/null @@ -1,18 +0,0 @@ -username: GitHub Actions -icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png - -pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." - -text: | - {{#if payload.commits}} - *Commits* - {{#each payload.commits}} - <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} - {{/each}} - {{/if}} - -footer: >- - <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} - -fallback: |- - [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Web._Blank/.github/workflows/build-and-deploy.yml b/Web._Blank/.github/workflows/build-and-deploy.yml deleted file mode 100644 index a9e645aa..00000000 --- a/Web._Blank/.github/workflows/build-and-deploy.yml +++ /dev/null @@ -1,171 +0,0 @@ -name: Build And Deploy -on: - push -env: - APP_NAME: Web.Blank - IMAGE_NAME: web.blank - SERVICE_NAME: web-blank - VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' - DOTNET_SDK_VERSION: 10.0 - DOTNET_ASPNET_VERSION: 10.0 - AZURE_GROUP: ${{ vars.AZURE_KUBERNETES_RESOURCE_GROUP }} - AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} - AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} - AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} - AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} - NUGET_HOST: ${{ secrets.NUGET_HOST }} - NUGET_USERNAME: ${{ secrets.NUGET_USERNAME }} - NUGET_PASSWORD: ${{ secrets.NUGET_APIKEY }} - NUGET_APIKEY: ${{ secrets.NUGET_APIKEY }} - CONTAINER_REGISTRY_HOST: ${{ vars.CONTAINER_REGISTRY_HOST }} - CONTAINER_REGISTRY_USERNAME: ${{ secrets.CONTAINER_REGISTRY_USERNAME }} - CONTAINER_REGISTRY_PASSWORD: ${{ secrets.CONTAINER_REGISTRY_PASSWORD }} - CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} - KUBERNETES_CLUSTER: ${{ github.ref == 'refs/heads/master' && vars.PRODUCTION_KUBERNETES_CLUSTER || vars.STAGING_KUBERNETES_CLUSTER }} - KUBERNETES_NODEPOOL_COMPUTE: cpu - KUBERNETES_NAMESPACE: default - KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} - KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} - KUBERNETES_REPLICA_HISTORY_COUNT: 0 - KUBERNETES_MEMORY_REQUEST: 512Mi - KUBERNETES_MEMORY_LIMIT: 1536Mi - KUBERNETES_MEMORY_SCALING: 180 - KUBERNETES_CPU_REQUEST: 200m - KUBERNETES_CPU_LIMIT: 600m - KUBERNETES_CPU_SCALING: 180 - ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} -jobs: - build-and-deploy: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - concurrency: - group: ${{ github.repository }} - cancel-in-progress: true - steps: - - uses: actions/checkout@v4 - - - name: Azure Login - shell: pwsh - run: | - sudo az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; - sudo az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; - sudo az aks get-credentials -g $env:AZURE_GROUP -n $env:KUBERNETES_CLUSTER --overwrite -o none; - - - name: Build - shell: pwsh - run: | - dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; - - dotnet build -c Release .\$env:APP_NAME.sln; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Test - shell: pwsh - run: | - dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish Image - shell: pwsh - run: | - $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() - $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; - $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION - - sudo docker build ` - -t $imageLatestTag ` - -t $imageVersionTag ` - --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` - --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` - --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` - --build-arg NUGET_HOST=$env:NUGET_HOST ` - --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` - --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` - ./; - - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - sudo docker login -u="$env:CONTAINER_REGISTRY_USERNAME" -p="$env:CONTAINER_REGISTRY_PASSWORD" $env:CONTAINER_REGISTRY_HOST; - sudo docker push $imageLatestTag; - sudo docker push $imageVersionTag; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Publish NuGet - shell: pwsh - run: | - $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; - dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; - dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_APIKEY; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: Kubernetes Deploy - shell: pwsh - run: | - Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; - sudo kubectl apply -f .kubernetes/service.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; - sudo kubectl apply -f .kubernetes/configmap.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; - sudo kubectl apply -f .kubernetes/deployment.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; - sudo kubectl apply -f .kubernetes/autoscaler.tmp.yaml; - if ($LastExitCode -ne 0) - { - throw "error"; - }; - - - name: GitHub Release - if: github.ref == 'refs/heads/master' - uses: ncipollo/release-action@v1 - with: - tag: v${{ env.VERSION }} - name: "Release ${{ env.VERSION }}" - body: | - Version: ${{ env.VERSION }} - Commit: ${{ github.sha }} - artifacts: "nupkgs/*" - token: ${{ secrets.GITHUB_TOKEN }} - draft: false - prerelease: false - - - name: Slack Notification - if: always() - uses: act10ns/slack@v2 - with: - webhook-url: ${{ secrets.SLACK_WEBHOOK }} - config: .github/config/slack.yml - status: ${{ job.status }} - channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Web._Blank/.gitignore b/Web._Blank/.gitignore deleted file mode 100644 index 9921fc06..00000000 --- a/Web._Blank/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -.vs -*.user -*.userprefs -*.suo -_ReSharper* -**/bin -**/obj -*.DotSettings.User -packages -.env -**/Properties/launchSettings.json \ No newline at end of file diff --git a/Web._Blank/.kubernetes/autoscaler.yaml b/Web._Blank/.kubernetes/autoscaler.yaml deleted file mode 100644 index 95add8ad..00000000 --- a/Web._Blank/.kubernetes/autoscaler.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: autoscaling/v2 -kind: HorizontalPodAutoscaler -metadata: - name: %SERVICE_NAME%-hpa - namespace: %KUBERNETES_NAMESPACE% -spec: - minReplicas: %KUBERNETES_REPLICA_COUNT% - maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: %SERVICE_NAME% - metrics: - - type: Resource - resource: - name: cpu - target: - type: Utilization - averageUtilization: %KUBERNETES_CPU_SCALING% - - type: Resource - resource: - name: memory - target: - type: Utilization - averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Web._Blank/.kubernetes/configmap.yaml b/Web._Blank/.kubernetes/configmap.yaml deleted file mode 100644 index 3977c0ee..00000000 --- a/Web._Blank/.kubernetes/configmap.yaml +++ /dev/null @@ -1,8 +0,0 @@ - apiVersion: v1 -kind: ConfigMap -metadata: - name: %SERVICE_NAME%-config - namespace: %KUBERNETES_NAMESPACE% -data: - App__Version: %VERSION% - ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Web._Blank/.kubernetes/deployment.yaml b/Web._Blank/.kubernetes/deployment.yaml deleted file mode 100644 index fdc5dfdf..00000000 --- a/Web._Blank/.kubernetes/deployment.yaml +++ /dev/null @@ -1,83 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% - labels: - app: %SERVICE_NAME% -spec: - replicas: %KUBERNETES_REPLICA_COUNT% - revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% - selector: - matchLabels: - app: %SERVICE_NAME% - template: - metadata: - labels: - app: %SERVICE_NAME% - spec: - automountServiceAccountToken: false - securityContext: - runAsUser: 1000 - runAsGroup: 2000 - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app: %SERVICE_NAME% - automountServiceAccountToken: false - nodeSelector: - nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% - kubernetes.io/os: linux - containers: - - name: %SERVICE_NAME% - image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% - ports: - - containerPort: 8080 - imagePullPolicy: Always - envFrom: - - configMapRef: - name: %SERVICE_NAME%-config - resources: - requests: - memory: %KUBERNETES_MEMORY_REQUEST% - cpu: %KUBERNETES_CPU_REQUEST% - limits: - memory: %KUBERNETES_MEMORY_LIMIT% - cpu: %KUBERNETES_CPU_LIMIT% - securityContext: - privileged: false - allowPrivilegeEscalation: false - readOnlyRootFilesystem: true - runAsNonRoot: true - runAsUser: 1000 - runAsGroup: 2000 - capabilities: - drop: - - ALL - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 10 - initialDelaySeconds: 30 - timeoutSeconds: 2 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - periodSeconds: 5 - initialDelaySeconds: 20 - timeoutSeconds: 2 - volumeMounts: - - name: tmp - mountPath: /tmp - - name: %SERVICE_NAME%-volume - mountPath: /mnt/%STORAGE_SHARE_NAME% - imagePullSecrets: - - name: ghcr-pull-secret - diff --git a/Web._Blank/.kubernetes/service.yaml b/Web._Blank/.kubernetes/service.yaml deleted file mode 100644 index 2d8e20b2..00000000 --- a/Web._Blank/.kubernetes/service.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: %SERVICE_NAME% - namespace: %KUBERNETES_NAMESPACE% -spec: - ports: - - name: http - port: 8080 - selector: - app: %SERVICE_NAME% - type: ClusterIP diff --git a/Web._Blank/.tests/Tests.Web.Blank/Properties/DoNotParallelize.cs b/Web._Blank/.tests/Tests.Web.Blank/Properties/DoNotParallelize.cs deleted file mode 100644 index 6a076669..00000000 --- a/Web._Blank/.tests/Tests.Web.Blank/Properties/DoNotParallelize.cs +++ /dev/null @@ -1,3 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Web._Blank/.tests/Tests.Web.Blank/Tests.Web.Blank.csproj b/Web._Blank/.tests/Tests.Web.Blank/Tests.Web.Blank.csproj deleted file mode 100644 index 75b187ba..00000000 --- a/Web._Blank/.tests/Tests.Web.Blank/Tests.Web.Blank.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - net10.0 - false - latest - - - true - - - - - - - - - - - - - - diff --git a/Web._Blank/Dockerfile b/Web._Blank/Dockerfile deleted file mode 100644 index 119abdb6..00000000 --- a/Web._Blank/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -ARG DOTNET_SDK_VERSION -ARG DOTNET_ASPNET_VERSION -ARG CONTAINER_REGISTRY_SOURCE_LABEL -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL - -EXPOSE 8080 -WORKDIR /app - -FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build -ARG NUGET_HOST -ARG NUGET_USERNAME -ARG NUGET_PASSWORD - -WORKDIR /src -COPY . . - -RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text -RUN dotnet build -c Release -o /app - -FROM build AS publish -RUN dotnet publish -c Release -o /app - -FROM base AS final -WORKDIR /app -COPY --from=publish /app . -ENV COMPlus_EnableDiagnostics=0 -ENV DOTNET_USE_POLLING_FILE_WATCHER=true - -ENTRYPOINT ["dotnet", "Web.Blank.dll"] \ No newline at end of file diff --git a/Web._Blank/LICENSE b/Web._Blank/LICENSE deleted file mode 100644 index 006e8c21..00000000 --- a/Web._Blank/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -The MIT License (MIT) -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/Web._Blank/README.md b/Web._Blank/README.md deleted file mode 100644 index f5864695..00000000 --- a/Web._Blank/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Web.Blank - -> _Minimal (blank) Nano Web application._ -_All lessons are complete, self-contained examples that include build and deployment setup._ - -> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. -Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ - -> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. - -*** - -## Table of Contents -* [Summary](#summary) - -## Summary -This application represents the most minimal (blank) Nano Web application setup. - -Its purpose is to demonstrate the required boilerplate, file structure, and general configuration needed to create a Nano Web application. -The application itself is intentionally minimal and does not expose HTTP endpoints or configure additional features. Instead, it serves as a baseline -from which all other Web lessons and examples are built. - -An `App.razor` has been added to show simple use of `IComponent` when registering razor pages. - -It is recommended to review this application first to understand how a Nano Web application is generally structured and to become familiar with the purpose of the core building blocks -used in the boilerplate. - -> 📖 Learn more about **[Nano Solution Composition](https://github.com/Nano-Core/Nano.Library#solution-composition)**. diff --git a/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj b/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj deleted file mode 100644 index df35117b..00000000 --- a/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj +++ /dev/null @@ -1,75 +0,0 @@ - - - - net10.0 - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - True - true - true - true - Michael Vivet - Michael Vivet - $(ProjectName) - Example project demonstrating a focused aspect of the Nano Library. - - A practical example highlighting a specific area or scenario of Nano for learning - and experimentation. Shows how features or patterns can be applied without - representing a full application or complete reference. - - - - Added .NET 10 support - - git - master - https://github.com/Nano-Core/Nano.Examples.git - $(GitCommitHash) - true - true - true - snupkg - true - $(Version) - nano;example;sample;framework;library;learning;showcase - https://github.com/Nano-Core/Nano.Library/$(ProjectName) - LICENSE - icon.png - README.md - true - - - - True - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Web._Blank/Web.Blank.sln b/Web._Blank/Web.Blank.sln deleted file mode 100644 index f81e7296..00000000 --- a/Web._Blank/Web.Blank.sln +++ /dev/null @@ -1,140 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 18 -VisualStudioVersion = 18.1.11312.151 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" - ProjectSection(SolutionItems) = preProject - .dockerignore = .dockerignore - .gitignore = .gitignore - Dockerfile = Dockerfile - icon.png = icon.png - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" - ProjectSection(SolutionItems) = preProject - .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml - .kubernetes\configmap.yaml = .kubernetes\configmap.yaml - .kubernetes\deployment.yaml = .kubernetes\deployment.yaml - .kubernetes\service.yaml = .kubernetes\service.yaml - EndProjectSection -EndProject -Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" - ProjectSection(SolutionItems) = preProject - .github\config\slack.yml = .github\config\slack.yml - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" - ProjectSection(SolutionItems) = preProject - .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Blank.Models", "Web.Blank.Models\Web.Blank.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Blank", "Web.Blank\Web.Blank.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Web.Blank", ".tests\Tests.Web.Blank\Tests.Web.Blank.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Web", "..\..\Nano.Library\Nano.App.Web\Nano.App.Web.csproj", "{AA77035F-A3A8-7972-CC88-17A2E41EBA87}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU - {AA77035F-A3A8-7972-CC88-17A2E41EBA87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA77035F-A3A8-7972-CC88-17A2E41EBA87}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA77035F-A3A8-7972-CC88-17A2E41EBA87}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA77035F-A3A8-7972-CC88-17A2E41EBA87}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} - {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} - {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} - {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} - {AA77035F-A3A8-7972-CC88-17A2E41EBA87} = {3339F5B7-AA56-4192-B201-75B2620036B1} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} - EndGlobalSection -EndGlobal diff --git a/Web._Blank/Web.Blank/App.razor b/Web._Blank/Web.Blank/App.razor deleted file mode 100644 index bc9fa62f..00000000 --- a/Web._Blank/Web.Blank/App.razor +++ /dev/null @@ -1,11 +0,0 @@ -@* App.razor *@ -

Hello from App root component

- - - - - - -

Sorry, there's nothing at this address.

-
-
\ No newline at end of file diff --git a/Web._Blank/Web.Blank/Dockerfile.Local b/Web._Blank/Web.Blank/Dockerfile.Local deleted file mode 100644 index 207af714..00000000 --- a/Web._Blank/Web.Blank/Dockerfile.Local +++ /dev/null @@ -1,6 +0,0 @@ -ARG DOTNET_ASPNET_VERSION="10.0" -FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base - -EXPOSE 8080 - -ENTRYPOINT ["dotnet", "Web.Blank.dll"] \ No newline at end of file diff --git a/Web._Blank/Web.Blank/Program.cs b/Web._Blank/Web.Blank/Program.cs deleted file mode 100644 index 73873dff..00000000 --- a/Web._Blank/Web.Blank/Program.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Nano.App.Web; -using Web.Blank; - -NanoWebApplication - .ConfigureApp() - .ConfigureServices(_ => - { - // Blank - }) - .Build() - .Run(); \ No newline at end of file diff --git a/Web._Blank/Web.Blank/Properties/InternalsVisibleTo.cs b/Web._Blank/Web.Blank/Properties/InternalsVisibleTo.cs deleted file mode 100644 index b7695e63..00000000 --- a/Web._Blank/Web.Blank/Properties/InternalsVisibleTo.cs +++ /dev/null @@ -1,3 +0,0 @@ -using System.Runtime.CompilerServices; - -[assembly: InternalsVisibleTo("Tests.Web.Blank")] \ No newline at end of file diff --git a/Web._Blank/Web.Blank/Web.Blank.csproj b/Web._Blank/Web.Blank/Web.Blank.csproj deleted file mode 100644 index b6e99c72..00000000 --- a/Web._Blank/Web.Blank/Web.Blank.csproj +++ /dev/null @@ -1,36 +0,0 @@ - - - - net10.0 - 10.0.0.0 - 10.0.0.0 - 10.0.0.0 - AnyCPU - Debug;Release - latest - en-US - enable - LICENSE - true - - false - true - false - true - Linux - ..\docker-compose.dcproj - - - - True - - - - - - - - - - - \ No newline at end of file diff --git a/Web._Blank/Web.Blank/appsettings.Development.json b/Web._Blank/Web.Blank/appsettings.Development.json deleted file mode 100644 index 8593c62d..00000000 --- a/Web._Blank/Web.Blank/appsettings.Development.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Web._Blank/Web.Blank/appsettings.Production.json b/Web._Blank/Web.Blank/appsettings.Production.json deleted file mode 100644 index 8593c62d..00000000 --- a/Web._Blank/Web.Blank/appsettings.Production.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Web._Blank/Web.Blank/appsettings.Staging.json b/Web._Blank/Web.Blank/appsettings.Staging.json deleted file mode 100644 index 8593c62d..00000000 --- a/Web._Blank/Web.Blank/appsettings.Staging.json +++ /dev/null @@ -1,2 +0,0 @@ -{ -} \ No newline at end of file diff --git a/Web._Blank/Web.Blank/appsettings.json b/Web._Blank/Web.Blank/appsettings.json deleted file mode 100644 index e8d9ff25..00000000 --- a/Web._Blank/Web.Blank/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "App": { - "Version": "1.0.0.0", - "Hosting": { - "Root": "api", - "Http": { - "Ports": [ - 8080 - ] - } - } - } -} \ No newline at end of file diff --git a/Web._Blank/icon.png b/Web._Blank/icon.png deleted file mode 100644 index 67567667de4c5a4ca797c66e71aa87aafc6df1e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~
Date: Thu, 4 Jun 2026 18:26:50 +0200 Subject: [PATCH 2/2] Added v10.0.0-rc1 branch --- .github/FUNDING.yml | 3 + .github/workflows/build-and-deploy.yml | 34 + .gitignore | 11 + .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 44 + Api.ApiClients.Audit/.dockerignore | 12 + Api.ApiClients.Audit/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.ApiClients.Audit/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ Api.ApiClients.Audit/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.ApiClients.Audit.csproj | 23 + .../Api.ApiClients.Audit.Models.csproj | 75 ++ ...Api.ApiClients.Audit.Service.Models.csproj | 77 ++ .../ApiClient/NanoApiClient.cs | 8 + .../Criterias/ExampleQueryCriteria.cs | 34 + .../Example.cs | 28 + .../ExampleNavigation.cs | 21 + .../Api.ApiClients.Audit.Service.csproj | 37 + .../Controllers/AuditController.cs | 9 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 34 + .../Data/Mappings/ExampleNavigationMapping.cs | 27 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260415133755_Initial.Designer.cs | 728 +++++++++++++++ .../Migrations/20260415133755_Initial.cs | 667 ++++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 725 +++++++++++++++ .../Api.ApiClients.Audit.Service/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 16 + Api.ApiClients.Audit/Api.ApiClients.Audit.sln | 159 ++++ .../Api.ApiClients.Audit.csproj | 38 + .../Controllers/AuditController.cs | 221 +++++ .../Controllers/ExamplesController.cs | 53 ++ .../Api.ApiClients.Audit/Dockerfile.Local | 6 + .../Api.ApiClients.Audit/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.ApiClients.Audit/appsettings.json | 26 + Api.ApiClients.Audit/Dockerfile | 35 + Api.ApiClients.Audit/LICENSE | 18 + Api.ApiClients.Audit/README.md | 42 + Api.ApiClients.Audit/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 44 + Api.ApiClients.Entity/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.ApiClients.Entity/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.ApiClients.Entity.csproj | 23 + .../Api.ApiClients.Entity.Models.csproj | 75 ++ ...pi.ApiClients.Entity.Service.Models.csproj | 77 ++ .../Api/NanoApiClient.cs | 8 + .../Criterias/ExampleQueryCriteria.cs | 34 + .../Example.cs | 14 + .../Api.ApiClients.Entity.Service.csproj | 37 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260422105215_Initial.Designer.cs | 651 ++++++++++++++ .../Migrations/20260422105215_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 648 +++++++++++++ .../Api.ApiClients.Entity.Service/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 16 + .../Api.ApiClients.Entity.sln | 159 ++++ .../Api.ApiClients.Entity.csproj | 38 + .../Controllers/ExamplesController.cs | 732 +++++++++++++++ .../Api.ApiClients.Entity/Dockerfile.Local | 6 + .../Api.ApiClients.Entity/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.ApiClients.Entity/appsettings.json | 25 + Api.ApiClients.Entity/Dockerfile | 35 + Api.ApiClients.Entity/LICENSE | 18 + Api.ApiClients.Entity/README.md | 30 + Api.ApiClients.Entity/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 29 + Api.ApiClients.RootLogIn/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.ApiClients.RootLogIn/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.ApiClients.RootLogIn.csproj | 23 + .../Api.ApiClients.RootLogIn.Models.csproj | 75 ++ .../Api.ApiClients.RootLogIn.sln | 159 ++++ ...ApiClients.RootLogin.Service.Models.csproj | 75 ++ .../ApiClient/NanoApiClient.cs | 24 + .../Requests/AutoAuthenticateRootRequest.cs | 9 + .../ApiClient/Requests/BaseCustomsRequest.cs | 17 + .../Api.ApiClients.RootLogin.Service.csproj | 37 + .../Controllers/AuthController.cs | 10 + .../Controllers/CustomsController.cs | 39 + .../Dockerfile.Local | 6 + .../Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 26 + .../Api.ApiClients.RootLogIn.csproj | 38 + .../Controllers/ExamplesController.cs | 41 + .../Api.ApiClients.RootLogin/Dockerfile.Local | 6 + .../Api.ApiClients.RootLogin/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.ApiClients.RootLogin/appsettings.json | 30 + Api.ApiClients.RootLogIn/Dockerfile | 35 + Api.ApiClients.RootLogIn/LICENSE | 18 + Api.ApiClients.RootLogIn/README.md | 31 + Api.ApiClients.RootLogIn/icon.png | Bin 0 -> 14103 bytes Api.ApiClients/.docker/docker-compose.dcproj | 13 + Api.ApiClients/.docker/docker-compose.yml | 29 + Api.ApiClients/.dockerignore | 12 + Api.ApiClients/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.ApiClients/.gitignore | 11 + Api.ApiClients/.kubernetes/autoscaler.yaml | 25 + Api.ApiClients/.kubernetes/configmap.yaml | 8 + Api.ApiClients/.kubernetes/deployment.yaml | 78 ++ Api.ApiClients/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.ApiClients.csproj | 23 + .../Api.ApiClients.Models.csproj | 75 ++ .../Api.ApiClients.Service.Models.csproj | 75 ++ .../Api/NanoApiClient.cs | 92 ++ .../Requests/BadRequestExceptionRequest.cs | 9 + .../Api/Requests/BaseCustomsRequest.cs | 17 + .../Api/Requests/CustomFileBodyRequest.cs | 25 + .../Api/Requests/CustomFileRequest.cs | 18 + .../Api/Requests/CustomRequest.cs | 43 + .../Api/Requests/Models/CustomBody.cs | 12 + .../Api/Requests/Models/CustomFileBody.cs | 15 + .../ProblemDetailsExceptionRequest.cs | 9 + .../Api/Requests/RequestTracingRequest.cs | 9 + .../Api/Responses/CustomFileBodyResponse.cs | 14 + .../Api/Responses/CustomFileResponse.cs | 12 + .../Api/Responses/CustomResponse.cs | 36 + .../Api/Responses/RequestTracingResponse.cs | 12 + .../Api.ApiClients.Service.csproj | 37 + .../Controllers/CustomsController.cs | 158 ++++ .../Api.ApiClients.Service/Dockerfile.Local | 6 + .../Api.ApiClients.Service/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.ApiClients.Service/appsettings.json | 13 + Api.ApiClients/Api.ApiClients.sln | 159 ++++ .../Api.ApiClients/Api.ApiClients.csproj | 38 + .../Controllers/ExamplesController.cs | 184 ++++ .../Api.ApiClients/Dockerfile.Local | 6 + Api.ApiClients/Api.ApiClients/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Api.ApiClients/appsettings.Staging.json | 2 + .../Api.ApiClients/appsettings.json | 26 + Api.ApiClients/Dockerfile | 35 + Api.ApiClients/LICENSE | 18 + Api.ApiClients/README.md | 64 ++ Api.ApiClients/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.Auth.External.Custom/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 188 ++++ Api.Auth.External.Custom/.gitignore | 10 + .../.kubernetes/auth-jwt-secret.yaml | 10 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 89 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Auth.External.Custom.csproj | 23 + .../Api.Auth.External.Custom.Models.csproj | 75 ++ .../Api.Auth.External.Custom.sln | 134 +++ .../Api.Auth.External.Custom.csproj | 37 + .../ExternalProviderCustomRepository.cs | 44 + .../Controllers/AuthController.cs | 10 + .../Controllers/ExamplesController.cs | 31 + .../Api.Auth.External.Custom/Dockerfile.Local | 6 + .../Api.Auth.External.Custom/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 22 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Auth.External.Custom/appsettings.json | 25 + Api.Auth.External.Custom/Dockerfile | 35 + Api.Auth.External.Custom/LICENSE | 18 + Api.Auth.External.Custom/README.md | 116 +++ Api.Auth.External.Custom/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Auth.RootLogin/.docker/docker-compose.yml | 17 + Api.Auth.RootLogin/.dockerignore | 12 + Api.Auth.RootLogin/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 188 ++++ Api.Auth.RootLogin/.gitignore | 11 + .../.kubernetes/auth-jwt-secret.yaml | 10 + .../.kubernetes/autoscaler.yaml | 25 + Api.Auth.RootLogin/.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 89 ++ Api.Auth.RootLogin/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Auth.RootLogin.csproj | 23 + .../Api.Auth.RootLogin.Models.csproj | 75 ++ Api.Auth.RootLogin/Api.Auth.RootLogin.sln | 134 +++ .../Api.Auth.RootLogin.csproj | 37 + .../Controllers/AuthController.cs | 10 + .../Controllers/ExamplesController.cs | 31 + .../Api.Auth.RootLogin/Dockerfile.Local | 6 + .../Api.Auth.RootLogin/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 17 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Auth.RootLogin/appsettings.json | 25 + Api.Auth.RootLogin/Dockerfile | 35 + Api.Auth.RootLogin/LICENSE | 18 + Api.Auth.RootLogin/README.md | 124 +++ Api.Auth.RootLogin/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Authorization/.docker/docker-compose.yml | 17 + Api.Authorization/.dockerignore | 12 + Api.Authorization/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 188 ++++ Api.Authorization/.gitignore | 11 + .../.kubernetes/auth-jwt-secret.yaml | 10 + Api.Authorization/.kubernetes/autoscaler.yaml | 25 + Api.Authorization/.kubernetes/configmap.yaml | 8 + Api.Authorization/.kubernetes/deployment.yaml | 89 ++ Api.Authorization/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Authorization.csproj | 23 + .../Api.Authorization.Models.csproj | 75 ++ Api.Authorization/Api.Authorization.sln | 134 +++ .../Api.Authorization.csproj | 37 + .../Controllers/AuthController.cs | 10 + .../Controllers/ExamplesController.cs | 49 + .../Api.Authorization/Dockerfile.Local | 6 + .../Extensions/ServiceCollectionExtensions.cs | 20 + .../Api.Authorization/Program.cs | 11 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 26 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Authorization/appsettings.json | 25 + Api.Authorization/Dockerfile | 35 + Api.Authorization/LICENSE | 18 + Api.Authorization/README.md | 46 + Api.Authorization/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.ContentNegotiation/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.ContentNegotiation/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.ContentNegotiation.csproj | 23 + .../Api.ContentNegotiation.Models.csproj | 75 ++ .../Api.ContentNegotiation.sln | 133 +++ .../Api.ContentNegotiation.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.ContentNegotiation/Dockerfile.Local | 6 + .../Api.ContentNegotiation/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.ContentNegotiation/appsettings.json | 13 + Api.ContentNegotiation/Dockerfile | 35 + Api.ContentNegotiation/LICENSE | 18 + Api.ContentNegotiation/README.md | 30 + Api.ContentNegotiation/icon.png | Bin 0 -> 14103 bytes Api.Cookies/.docker/docker-compose.dcproj | 13 + Api.Cookies/.docker/docker-compose.yml | 17 + Api.Cookies/.dockerignore | 12 + Api.Cookies/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Cookies/.gitignore | 11 + Api.Cookies/.kubernetes/autoscaler.yaml | 25 + Api.Cookies/.kubernetes/configmap.yaml | 8 + Api.Cookies/.kubernetes/deployment.yaml | 78 ++ Api.Cookies/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Cookies.csproj | 23 + .../Api.Cookies.Models.csproj | 75 ++ Api.Cookies/Api.Cookies.sln | 133 +++ Api.Cookies/Api.Cookies/Api.Cookies.csproj | 37 + .../Controllers/ExamplesController.cs | 91 ++ Api.Cookies/Api.Cookies/Dockerfile.Local | 6 + Api.Cookies/Api.Cookies/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Api.Cookies/appsettings.Development.json | 2 + .../Api.Cookies/appsettings.Production.json | 2 + .../Api.Cookies/appsettings.Staging.json | 2 + Api.Cookies/Api.Cookies/appsettings.json | 13 + Api.Cookies/Dockerfile | 35 + Api.Cookies/LICENSE | 18 + Api.Cookies/README.md | 33 + Api.Cookies/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.CustomConfigSection/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.CustomConfigSection/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.CustomConfigSection.csproj | 23 + .../Api.CustomConfigSection.Models.csproj | 75 ++ .../Api.CustomConfigSection.sln | 133 +++ .../Api.CustomConfigSection.csproj | 37 + .../Config/CustomOptions.cs | 17 + .../Controllers/ExamplesController.cs | 37 + .../Api.CustomConfigSection/Dockerfile.Local | 6 + .../Api.CustomConfigSection/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.CustomConfigSection/appsettings.json | 16 + Api.CustomConfigSection/Dockerfile | 35 + Api.CustomConfigSection/LICENSE | 18 + Api.CustomConfigSection/README.md | 65 ++ Api.CustomConfigSection/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.CustomMiddleware/.dockerignore | 12 + Api.CustomMiddleware/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.CustomMiddleware/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ Api.CustomMiddleware/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.CustomMiddleware.csproj | 23 + .../Api.CustomMiddleware.Models.csproj | 75 ++ Api.CustomMiddleware/Api.CustomMiddleware.sln | 133 +++ .../Api.CustomMiddleware.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.CustomMiddleware/Dockerfile.Local | 6 + .../Api.CustomMiddleware/Program.cs | 19 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.CustomMiddleware/appsettings.json | 13 + Api.CustomMiddleware/Dockerfile | 35 + Api.CustomMiddleware/LICENSE | 18 + Api.CustomMiddleware/README.md | 49 + Api.CustomMiddleware/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.CustomService/.docker/docker-compose.yml | 17 + Api.CustomService/.dockerignore | 12 + Api.CustomService/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.CustomService/.gitignore | 11 + Api.CustomService/.kubernetes/autoscaler.yaml | 25 + Api.CustomService/.kubernetes/configmap.yaml | 8 + Api.CustomService/.kubernetes/deployment.yaml | 78 ++ Api.CustomService/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.CustomService.csproj | 23 + .../Api.CustomService.Models.csproj | 75 ++ Api.CustomService/Api.CustomService.sln | 133 +++ .../Api.CustomService.csproj | 37 + .../Controllers/ExamplesController.cs | 37 + .../Api.CustomService/Dockerfile.Local | 6 + .../Api.CustomService/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../Services/Abstractions/IExampleService.cs | 15 + .../Services/ExampleService.cs | 16 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.CustomService/appsettings.json | 13 + Api.CustomService/Dockerfile | 35 + Api.CustomService/LICENSE | 18 + Api.CustomService/README.md | 44 + Api.CustomService/icon.png | Bin 0 -> 14103 bytes Api.Data.Audit/.docker/docker-compose.dcproj | 13 + Api.Data.Audit/.docker/docker-compose.yml | 32 + Api.Data.Audit/.dockerignore | 12 + Api.Data.Audit/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.Audit/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + Api.Data.Audit/.kubernetes/autoscaler.yaml | 25 + Api.Data.Audit/.kubernetes/configmap.yaml | 8 + Api.Data.Audit/.kubernetes/deployment.yaml | 84 ++ Api.Data.Audit/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.Audit.csproj | 23 + .../Api.Data.Audit.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Api.Data.Audit.Models/Example.cs | 28 + .../ExampleNavigation.cs | 21 + .../Api.Data.Audit.Models/ExampleNoAudit.cs | 14 + Api.Data.Audit/Api.Data.Audit.sln | 148 +++ .../Api.Data.Audit/Api.Data.Audit.csproj | 37 + .../Controllers/AuditController.cs | 9 + .../Controllers/ExampleNoAuditsController.cs | 15 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 34 + .../Data/Mappings/ExampleNavigationMapping.cs | 27 + .../Data/Mappings/ExampleNoAuditMapping.cs | 23 + .../Api.Data.Audit/Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.Audit/Dockerfile.Local | 6 + .../20260415133755_Initial.Designer.cs | 728 +++++++++++++++ .../Migrations/20260415133755_Initial.cs | 667 ++++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 725 +++++++++++++++ Api.Data.Audit/Api.Data.Audit/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../Api.Data.Audit/appsettings.Staging.json | 2 + .../Api.Data.Audit/appsettings.json | 18 + Api.Data.Audit/Dockerfile | 35 + Api.Data.Audit/LICENSE | 18 + Api.Data.Audit/README.md | 37 + Api.Data.Audit/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 62 ++ Api.Data.EntityEvents/.docker/init.sql | 9 + Api.Data.EntityEvents/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.EntityEvents/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 95 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.EntityEvents.csproj | 23 + .../Api.Data.EntityEvents.Models/Address.cs | 21 + .../Api.Data.EntityEvents.Models.csproj | 79 ++ .../Criterias/DefaultQueryCriteria.cs | 17 + .../Api.Data.EntityEvents.Models/Customer.cs | 43 + .../Api.Data.EntityEvents.Models/Order.cs | 22 + .../Owned/ProfileSettings.cs | 15 + .../Api.Data.EntityEvents.Models/Person.cs | 18 + .../Api.Data.EntityEvents.Models/Profile.cs | 35 + ...Data.EntityEvents.Subscriber.Models.csproj | 79 ++ .../Customer.cs | 47 + .../Api.Data.EntityEvents.Subscriber.csproj | 37 + .../Data/Mappings/CustomerMapping.cs | 41 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260414100646_Initial.Designer.cs | 665 ++++++++++++++ .../Migrations/20260414100646_Initial.cs | 603 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 662 ++++++++++++++ .../Program.cs | 16 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 23 + .../Api.Data.EntityEvents.sln | 177 ++++ .../Api.Data.EntityEvents.csproj | 37 + .../Controllers/AddresssController.cs | 15 + .../Controllers/CustomersController.cs | 15 + .../Controllers/OrdersController.cs | 15 + .../Controllers/ProfilesController.cs | 15 + .../Data/Mappings/AddressMapping.cs | 26 + .../Data/Mappings/CustomerMapping.cs | 48 + .../Data/Mappings/OrderMapping.cs | 23 + .../Data/Mappings/PersonMapping.cs | 23 + .../Data/Mappings/ProfileMapping.cs | 32 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.EntityEvents/Dockerfile.Local | 6 + .../20260412140549_Initial.Designer.cs | 849 ++++++++++++++++++ .../Migrations/20260412140549_Initial.cs | 756 ++++++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 846 +++++++++++++++++ .../Api.Data.EntityEvents/Program.cs | 16 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 12 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.EntityEvents/appsettings.json | 19 + Api.Data.EntityEvents/Dockerfile | 35 + Api.Data.EntityEvents/LICENSE | 18 + Api.Data.EntityEvents/README.md | 43 + Api.Data.EntityEvents/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.Identity.Auth.ApiKey/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 252 ++++++ Api.Data.Identity.Auth.ApiKey/.gitignore | 11 + .../.kubernetes/auth-apikey-secret.yaml | 9 + .../.kubernetes/auth-jwt-secret.yaml | 10 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 99 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ...Tests.Api.Data.Identity.Auth.ApiKey.csproj | 23 + ...pi.Data.Identity.Auth.ApiKey.Models.csproj | 77 ++ .../Criterias/UserQueryCriteria.cs | 34 + .../User.cs | 17 + .../Api.Data.Identity.Auth.ApiKey.sln | 150 ++++ .../Api.Data.Identity.Auth.ApiKey.csproj | 37 + .../Controllers/AuthController.cs | 9 + .../Controllers/UsersController.cs | 12 + .../Data/Mappings/UserMapping.cs | 24 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260415143026_Initial.Designer.cs | 660 ++++++++++++++ .../Migrations/20260415143026_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 657 ++++++++++++++ .../Api.Data.Identity.Auth.ApiKey/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 18 + .../appsettings.Production.json | 10 + .../appsettings.Staging.json | 10 + .../appsettings.json | 60 ++ Api.Data.Identity.Auth.ApiKey/Dockerfile | 35 + Api.Data.Identity.Auth.ApiKey/LICENSE | 18 + Api.Data.Identity.Auth.ApiKey/README.md | 86 ++ Api.Data.Identity.Auth.ApiKey/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + .../.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 244 +++++ .../.gitignore | 11 + .../.kubernetes/auth-jwt-secret.yaml | 10 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 94 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ....Data.Identity.Auth.External.Custom.csproj | 23 + ...dentity.Auth.External.Custom.Models.csproj | 77 ++ .../Criterias/UserQueryCriteria.cs | 34 + .../User.cs | 17 + ...Api.Data.Identity.Auth.External.Custom.sln | 149 +++ ....Data.Identity.Auth.External.Custom.csproj | 37 + .../ExternalProviderCustomRepository.cs | 44 + .../Controllers/AuthController.cs | 9 + .../Controllers/UsersController.cs | 12 + .../Data/Mappings/UserMapping.cs | 24 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260415143439_Initial.Designer.cs | 660 ++++++++++++++ .../Migrations/20260415143439_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 657 ++++++++++++++ .../Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 18 + .../appsettings.Production.json | 10 + .../appsettings.Staging.json | 10 + .../appsettings.json | 57 ++ .../Dockerfile | 35 + .../LICENSE | 18 + .../README.md | 86 ++ .../icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.Identity.Auth.Jwt/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 244 +++++ Api.Data.Identity.Auth.Jwt/.gitignore | 11 + .../.kubernetes/auth-jwt-secret.yaml | 10 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 94 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.Identity.Auth.Jwt.csproj | 23 + .../Api.Data.Identity.Auth.Jwt.Models.csproj | 77 ++ .../Criterias/UserQueryCriteria.cs | 34 + .../Api.Data.Identity.Auth.Jwt.Models/User.cs | 17 + .../Api.Data.Identity.Auth.Jwt.sln | 149 +++ .../Api.Data.Identity.Auth.Jwt.csproj | 37 + .../Controllers/AuthController.cs | 9 + .../Controllers/UsersController.cs | 12 + .../Data/Mappings/UserMapping.cs | 24 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260415143540_Initial.Designer.cs | 660 ++++++++++++++ .../Migrations/20260415143540_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 657 ++++++++++++++ .../Api.Data.Identity.Auth.Jwt/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 18 + .../appsettings.Production.json | 10 + .../appsettings.Staging.json | 10 + .../appsettings.json | 57 ++ Api.Data.Identity.Auth.Jwt/Dockerfile | 35 + Api.Data.Identity.Auth.Jwt/LICENSE | 18 + Api.Data.Identity.Auth.Jwt/README.md | 118 +++ Api.Data.Identity.Auth.Jwt/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Data.Identity/.docker/docker-compose.yml | 32 + Api.Data.Identity/.dockerignore | 12 + Api.Data.Identity/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.Identity/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + Api.Data.Identity/.kubernetes/autoscaler.yaml | 25 + Api.Data.Identity/.kubernetes/configmap.yaml | 8 + Api.Data.Identity/.kubernetes/deployment.yaml | 84 ++ Api.Data.Identity/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.Identity.csproj | 23 + .../Api.Data.Identity.Models.csproj | 77 ++ .../Criterias/UserQueryCriteria.cs | 34 + .../Api.Data.Identity.Models/User.cs | 17 + Api.Data.Identity/Api.Data.Identity.sln | 148 +++ .../Api.Data.Identity.csproj | 37 + .../Controllers/UsersController.cs | 12 + .../Data/Mappings/UserMapping.cs | 24 + .../Api.Data.Identity/Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.Identity/Dockerfile.Local | 6 + .../20260415142847_Initial.Designer.cs | 660 ++++++++++++++ .../Migrations/20260415142847_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 657 ++++++++++++++ .../Api.Data.Identity/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.Identity/appsettings.json | 47 + Api.Data.Identity/Dockerfile | 35 + Api.Data.Identity/LICENSE | 18 + Api.Data.Identity/README.md | 75 ++ Api.Data.Identity/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Data.InMemory/.docker/docker-compose.yml | 17 + Api.Data.InMemory/.dockerignore | 12 + Api.Data.InMemory/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Data.InMemory/.gitignore | 11 + Api.Data.InMemory/.kubernetes/autoscaler.yaml | 25 + Api.Data.InMemory/.kubernetes/configmap.yaml | 8 + Api.Data.InMemory/.kubernetes/deployment.yaml | 78 ++ Api.Data.InMemory/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.InMemory.csproj | 23 + .../Api.Data.InMemory.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Api.Data.InMemory.Models/Example.cs | 14 + .../ExampleCreatable.cs | 14 + .../ExampleCreatableAndEditable.cs | 14 + .../ExampleDeletable.cs | 14 + .../ExampleUpdatable.cs | 14 + Api.Data.InMemory/Api.Data.InMemory.sln | 147 +++ .../Api.Data.InMemory.csproj | 37 + .../ExampleCreatableAndEditableController.cs | 15 + .../ExampleCreateablesController.cs | 15 + .../ExampleDeletablesController.cs | 15 + .../ExampleUpdatablesController.cs | 15 + .../Controllers/ExamplesController.cs | 15 + .../Data/InMemoryDbContext.cs | 11 + .../ExampleCreatableAndUpdatableMapping.cs | 26 + .../Data/Mappings/ExampleCreatableMapping.cs | 26 + .../Data/Mappings/ExampleDeletableMapping.cs | 26 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/Mappings/ExampleUpdatableMapping.cs | 26 + .../Api.Data.InMemory/Dockerfile.Local | 6 + .../Api.Data.InMemory/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.InMemory/appsettings.json | 37 + Api.Data.InMemory/Dockerfile | 35 + Api.Data.InMemory/LICENSE | 18 + Api.Data.InMemory/README.md | 78 ++ Api.Data.InMemory/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.LazyLoading/.dockerignore | 12 + Api.Data.LazyLoading/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.LazyLoading/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ Api.Data.LazyLoading/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.LazyLoading.csproj | 23 + .../Api.Data.LazyLoading.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 17 + .../Api.Data.LazyLoading.Models/Example.cs | 27 + .../ExampleRelation.cs | 27 + .../ExampleRelationIncluded.cs | 27 + Api.Data.LazyLoading/Api.Data.LazyLoading.sln | 148 +++ .../Api.Data.LazyLoading.csproj | 37 + .../Controllers/ExamplesController.cs | 45 + .../Data/Mappings/ExampleMapping.cs | 36 + .../ExampleRelationIncludedMapping.cs | 28 + .../Data/Mappings/ExampleRelationMapping.cs | 28 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.LazyLoading/Dockerfile.Local | 6 + .../20260415143541_Initial.Designer.cs | 748 +++++++++++++++ .../Migrations/20260415143541_Initial.cs | 685 ++++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 745 +++++++++++++++ .../Api.Data.LazyLoading/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.LazyLoading/appsettings.json | 17 + Api.Data.LazyLoading/Dockerfile | 35 + Api.Data.LazyLoading/LICENSE | 18 + Api.Data.LazyLoading/README.md | 33 + Api.Data.LazyLoading/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.MySql.Collation/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.MySql.Collation/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.MySql.Collation.csproj | 23 + .../Api.Data.MySql.Collation.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Example.cs | 14 + .../Api.Data.MySql.Collation.sln | 148 +++ .../Api.Data.MySql.Collation.csproj | 37 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/MySqlDbContext.cs | 11 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.MySql.Collation/Dockerfile.Local | 6 + .../20260415143932_Initial.Designer.cs | 651 ++++++++++++++ .../Migrations/20260415143932_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 648 +++++++++++++ .../Api.Data.MySql.Collation/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.MySql.Collation/appsettings.json | 16 + Api.Data.MySql.Collation/Dockerfile | 35 + Api.Data.MySql.Collation/LICENSE | 18 + Api.Data.MySql.Collation/README.md | 37 + Api.Data.MySql.Collation/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.MySql.Mappings/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.MySql.Mappings/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.MySql.Mappings.csproj | 23 + .../Api.Data.MySql.Mappings.Models.csproj | 77 ++ .../ExampleNormalizedQueryCriteria.cs | 34 + .../Criterias/ExampleQueryCriteria.cs | 17 + .../ExampleJson.cs | 17 + .../ExampleNormalized.cs | 65 ++ .../ExampleOwned.cs | 17 + .../Types/Profile.cs | 25 + .../Types/ProfilePicture.cs | 19 + .../Types/ProfileSettings.cs | 24 + .../Api.Data.MySql.Mappings.sln | 148 +++ .../Api.Data.MySql.Mappings.csproj | 37 + .../Controllers/ExampleJsonController.cs | 15 + .../ExampleNormalizedController.cs | 15 + .../Controllers/ExampleOwnedController.cs | 15 + .../Data/Mappings/ExampleJsonMapping.cs | 28 + .../Data/Mappings/ExampleNormalizedMapping.cs | 44 + .../Data/Mappings/ExampleOwnedMapping.cs | 24 + .../Extensions/EntityTypeBuilderExtensions.cs | 41 + .../OwnedNavigationBuilderExtensions.cs | 70 ++ .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.MySql.Mappings/Dockerfile.Local | 6 + .../20260415143933_Initial.Designer.cs | 794 ++++++++++++++++ .../Migrations/20260415143933_Initial.cs | 673 ++++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 791 ++++++++++++++++ .../Api.Data.MySql.Mappings/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.MySql.Mappings/appsettings.json | 16 + Api.Data.MySql.Mappings/Dockerfile | 35 + Api.Data.MySql.Mappings/LICENSE | 18 + Api.Data.MySql.Mappings/README.md | 30 + Api.Data.MySql.Mappings/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.MySql.Spatial/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.MySql.Spatial/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.MySql.Spatial.csproj | 23 + .../Api.Data.MySql.Spatial.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 46 + .../Api.Data.MySql.Spatial.Models/Example.cs | 22 + .../Api.Data.MySql.Spatial.sln | 148 +++ .../Api.Data.MySql.Spatial.csproj | 37 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 39 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.MySql.Spatial/Dockerfile.Local | 6 + .../20260415143931_Initial.Designer.cs | 662 ++++++++++++++ .../Migrations/20260415143931_Initial.cs | 610 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 659 ++++++++++++++ .../Api.Data.MySql.Spatial/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.MySql.Spatial/appsettings.json | 16 + Api.Data.MySql.Spatial/Dockerfile | 35 + Api.Data.MySql.Spatial/LICENSE | 18 + Api.Data.MySql.Spatial/README.md | 25 + Api.Data.MySql.Spatial/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.MySql.StoredProcedures/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.MySql.StoredProcedures/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ...sts.Api.Data.MySql.StoredProcedures.csproj | 23 + ....Data.MySql.StoredProcedures.Models.csproj | 77 ++ .../Example.cs | 23 + .../ExampleResult.cs | 26 + .../Api.Data.MySql.StoredProcedures.sln | 148 +++ .../Api.Data.MySql.StoredProcedures.csproj | 37 + .../Controllers/ExamplesController.cs | 38 + .../Data/Extensions/RepositoryExtensions.cs | 28 + .../Data/Mappings/ExampleMapping.cs | 32 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Data/StoredProcedures/StoredProcedures.cs | 28 + .../Dockerfile.Local | 6 + .../20260415151833_Initial.Designer.cs | 656 ++++++++++++++ .../Migrations/20260415151833_Initial.cs | 602 +++++++++++++ ...415151941_AddedStoredProcedure.Designer.cs | 656 ++++++++++++++ .../20260415151941_AddedStoredProcedure.cs | 24 + .../Migrations/MySqlDbContextModelSnapshot.cs | 653 ++++++++++++++ .../Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 16 + Api.Data.MySql.StoredProcedures/Dockerfile | 35 + Api.Data.MySql.StoredProcedures/LICENSE | 18 + Api.Data.MySql.StoredProcedures/README.md | 38 + Api.Data.MySql.StoredProcedures/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.MySql.Views/.dockerignore | 12 + Api.Data.MySql.Views/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.MySql.Views/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ Api.Data.MySql.Views/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.MySql.Views.csproj | 23 + .../Api.Data.MySql.Views.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Api.Data.MySql.Views.Models/Example.cs | 14 + .../ExampleView.cs | 30 + Api.Data.MySql.Views/Api.Data.MySql.Views.sln | 148 +++ .../Api.Data.MySql.Views.csproj | 37 + .../Controllers/ExampleViewsController.cs | 15 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 23 + .../Data/Mappings/ExampleViewMapping.cs | 32 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Data/Views/ExampleViewDefinition.cs | 15 + .../Api.Data.MySql.Views/Dockerfile.Local | 6 + .../20260415152009_Initial.Designer.cs | 669 ++++++++++++++ .../Migrations/20260415152009_Initial.cs | 596 ++++++++++++ .../20260415152149_AddedView.Designer.cs | 669 ++++++++++++++ .../Migrations/20260415152149_AddedView.cs | 24 + .../Migrations/MySqlDbContextModelSnapshot.cs | 666 ++++++++++++++ .../Api.Data.MySql.Views/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.MySql.Views/appsettings.json | 16 + Api.Data.MySql.Views/Dockerfile | 35 + Api.Data.MySql.Views/LICENSE | 18 + Api.Data.MySql.Views/README.md | 32 + Api.Data.MySql.Views/icon.png | Bin 0 -> 14103 bytes Api.Data.MySql/.docker/docker-compose.dcproj | 13 + Api.Data.MySql/.docker/docker-compose.yml | 32 + Api.Data.MySql/.dockerignore | 12 + Api.Data.MySql/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.MySql/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + Api.Data.MySql/.kubernetes/autoscaler.yaml | 25 + Api.Data.MySql/.kubernetes/configmap.yaml | 8 + Api.Data.MySql/.kubernetes/deployment.yaml | 84 ++ Api.Data.MySql/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.MySql.csproj | 23 + .../Api.Data.MySql.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Api.Data.MySql.Models/Example.cs | 14 + .../Api.Data.MySql.Models/ExampleCreatable.cs | 14 + .../ExampleCreatableAndEditable.cs | 14 + .../Api.Data.MySql.Models/ExampleDeletable.cs | 14 + .../Api.Data.MySql.Models/ExampleUpdatable.cs | 14 + Api.Data.MySql/Api.Data.MySql.sln | 148 +++ .../Api.Data.MySql/Api.Data.MySql.csproj | 37 + .../ExampleCreatableAndEditableController.cs | 15 + .../ExampleCreateablesController.cs | 15 + .../ExampleDeletablesController.cs | 15 + .../ExampleUpdatablesController.cs | 15 + .../Controllers/ExamplesController.cs | 15 + .../ExampleCreatableAndUpdatableMapping.cs | 26 + .../Data/Mappings/ExampleCreatableMapping.cs | 26 + .../Data/Mappings/ExampleDeletableMapping.cs | 26 + .../Data/Mappings/ExampleMapping.cs | 27 + .../Data/Mappings/ExampleUpdatableMapping.cs | 26 + .../Api.Data.MySql/Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.MySql/Dockerfile.Local | 6 + .../20260423071339_Initial.Designer.cs | 781 ++++++++++++++++ .../Migrations/20260423071339_Initial.cs | 742 +++++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 778 ++++++++++++++++ Api.Data.MySql/Api.Data.MySql/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../Api.Data.MySql/appsettings.Staging.json | 2 + .../Api.Data.MySql/appsettings.json | 39 + Api.Data.MySql/Dockerfile | 35 + Api.Data.MySql/LICENSE | 18 + Api.Data.MySql/README.md | 189 ++++ Api.Data.MySql/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 30 + Api.Data.PostgreSQL.Spatial/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 243 +++++ Api.Data.PostgreSQL.Spatial/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.PostgreSQL.Spatial.csproj | 23 + .../Api.Data.PostgreSQL.Spatial.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 46 + .../Example.cs | 22 + .../Api.Data.PostgreSQL.Spatial.sln | 148 +++ .../Api.Data.PostgreSQL.Spatial.csproj | 37 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 38 + .../Data/PostgreSqlDbContext.cs | 10 + .../Data/PostgreSqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260415150018_Initial.Designer.cs | 661 ++++++++++++++ .../Migrations/20260415150018_Initial.cs | 552 ++++++++++++ .../PostgreSqlDbContextModelSnapshot.cs | 658 ++++++++++++++ .../Api.Data.PostgreSQL.Spatial/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 16 + Api.Data.PostgreSQL.Spatial/Dockerfile | 35 + Api.Data.PostgreSQL.Spatial/LICENSE | 18 + Api.Data.PostgreSQL.Spatial/README.md | 25 + Api.Data.PostgreSQL.Spatial/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 30 + Api.Data.PostgreSQL/.dockerignore | 12 + Api.Data.PostgreSQL/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 243 +++++ Api.Data.PostgreSQL/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ Api.Data.PostgreSQL/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.PostgreSQL.csproj | 23 + .../Api.Data.PostgreSQL.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Api.Data.PostgreSQL.Models/Example.cs | 14 + .../ExampleCreatable.cs | 14 + .../ExampleCreatableAndEditable.cs | 14 + .../ExampleDeletable.cs | 14 + .../ExampleUpdatable.cs | 14 + Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln | 148 +++ .../Api.Data.PostgreSQL.csproj | 37 + .../ExampleCreatableAndEditableController.cs | 15 + .../ExampleCreateablesController.cs | 15 + .../ExampleDeletablesController.cs | 15 + .../ExampleUpdatablesController.cs | 15 + .../Controllers/ExamplesController.cs | 15 + .../ExampleCreatableAndUpdatableMapping.cs | 26 + .../Data/Mappings/ExampleCreatableMapping.cs | 26 + .../Data/Mappings/ExampleDeletableMapping.cs | 26 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/Mappings/ExampleUpdatableMapping.cs | 26 + .../Data/PostgreSqlDbContext.cs | 10 + .../Data/PostgreSqlDbContextFactory.cs | 7 + .../Api.Data.PostgreSQL/Dockerfile.Local | 6 + .../20260415145934_Initial.Designer.cs | 770 ++++++++++++++++ .../Migrations/20260415145934_Initial.cs | 672 ++++++++++++++ .../PostgreSqlDbContextModelSnapshot.cs | 767 ++++++++++++++++ .../Api.Data.PostgreSQL/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.PostgreSQL/appsettings.json | 39 + Api.Data.PostgreSQL/Dockerfile | 35 + Api.Data.PostgreSQL/LICENSE | 18 + Api.Data.PostgreSQL/README.md | 204 +++++ Api.Data.PostgreSQL/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.Repository.AutoSave/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.Repository.AutoSave/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.Repository.AutoSave.csproj | 23 + ...Api.Data.Repository.AutoSave.Models.csproj | 77 ++ .../Example.cs | 14 + .../Api.Data.Repository.AutoSave.sln | 148 +++ .../Api.Data.Repository.AutoSave.csproj | 37 + .../Controllers/ExamplesController.cs | 39 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260415150030_Initial.Designer.cs | 651 ++++++++++++++ .../Migrations/20260415150030_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 648 +++++++++++++ .../Api.Data.Repository.AutoSave/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 19 + Api.Data.Repository.AutoSave/Dockerfile | 35 + Api.Data.Repository.AutoSave/LICENSE | 18 + Api.Data.Repository.AutoSave/README.md | 44 + Api.Data.Repository.AutoSave/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.Repository.Includes/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.Repository.Includes/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.Repository.Includes.csproj | 23 + ...Api.Data.Repository.Includes.Models.csproj | 77 ++ .../Customer.cs | 33 + .../CustomerProfile.cs | 21 + .../Order.cs | 34 + .../Payment.cs | 14 + .../Api.Data.Repository.Includes.sln | 148 +++ .../Api.Data.Repository.Includes.csproj | 37 + .../Controllers/ExamplesController.cs | 146 +++ .../Response/CustomerProfileResponse.cs | 19 + .../Controllers/Response/CustomerResponse.cs | 25 + .../Controllers/Response/OrderResponse.cs | 24 + .../Controllers/Response/PaymentResponse.cs | 14 + .../Data/Mappings/CustomerMapping.cs | 30 + .../Data/Mappings/CustomerProfileMapping.cs | 28 + .../Data/Mappings/OrderMapping.cs | 29 + .../Data/Mappings/PaymentMapping.cs | 24 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260415150006_Initial.Designer.cs | 791 ++++++++++++++++ .../Migrations/20260415150006_Initial.cs | 717 +++++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 788 ++++++++++++++++ .../Api.Data.Repository.Includes/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 19 + Api.Data.Repository.Includes/Dockerfile | 35 + Api.Data.Repository.Includes/LICENSE | 18 + Api.Data.Repository.Includes/README.md | 55 ++ Api.Data.Repository.Includes/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 32 + Api.Data.SoftDelete/.dockerignore | 12 + Api.Data.SoftDelete/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.SoftDelete/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ Api.Data.SoftDelete/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.SoftDelete.csproj | 23 + .../Api.Data.SoftDelete.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Api.Data.SoftDelete.Models/Example.cs | 15 + Api.Data.SoftDelete/Api.Data.SoftDelete.sln | 148 +++ .../Api.Data.SoftDelete.csproj | 37 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 39 + .../Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Api.Data.SoftDelete/Dockerfile.Local | 6 + .../20260415151003_Initial.Designer.cs | 651 ++++++++++++++ .../Migrations/20260415151003_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 648 +++++++++++++ .../Api.Data.SoftDelete/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.SoftDelete/appsettings.json | 16 + Api.Data.SoftDelete/Dockerfile | 35 + Api.Data.SoftDelete/LICENSE | 18 + Api.Data.SoftDelete/README.md | 29 + Api.Data.SoftDelete/icon.png | Bin 0 -> 14103 bytes Api.Data.SqLite/.docker/docker-compose.dcproj | 13 + Api.Data.SqLite/.docker/docker-compose.yml | 18 + Api.Data.SqLite/.dockerignore | 12 + Api.Data.SqLite/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 209 +++++ Api.Data.SqLite/.gitignore | 11 + Api.Data.SqLite/.kubernetes/autoscaler.yaml | 25 + Api.Data.SqLite/.kubernetes/configmap.yaml | 8 + Api.Data.SqLite/.kubernetes/data-pvc.yaml | 11 + .../.kubernetes/data-storageclass.yaml | 10 + Api.Data.SqLite/.kubernetes/deployment.yaml | 85 ++ Api.Data.SqLite/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.SqLite.csproj | 23 + .../Api.Data.SqLite.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Api.Data.SqLite.Models/Example.cs | 14 + .../ExampleCreatable.cs | 14 + .../ExampleCreatableAndEditable.cs | 14 + .../ExampleDeletable.cs | 14 + .../ExampleUpdatable.cs | 14 + Api.Data.SqLite/Api.Data.SqLite.sln | 149 +++ .../Api.Data.SqLite/Api.Data.SqLite.csproj | 37 + .../ExampleCreatableAndEditableController.cs | 15 + .../ExampleCreateablesController.cs | 15 + .../ExampleDeletablesController.cs | 15 + .../ExampleUpdatablesController.cs | 15 + .../Controllers/ExamplesController.cs | 15 + .../ExampleCreatableAndUpdatableMapping.cs | 26 + .../Data/Mappings/ExampleCreatableMapping.cs | 26 + .../Data/Mappings/ExampleDeletableMapping.cs | 26 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/Mappings/ExampleUpdatableMapping.cs | 26 + .../Api.Data.SqLite/Data/SqLiteDbContext.cs | 10 + .../Data/SqLiteDbContextFactory.cs | 7 + .../Api.Data.SqLite/Dockerfile.Local | 6 + .../20260427141022_Initial.Designer.cs | 758 ++++++++++++++++ .../Migrations/20260427141022_Initial.cs | 668 ++++++++++++++ .../SqLiteDbContextModelSnapshot.cs | 755 ++++++++++++++++ Api.Data.SqLite/Api.Data.SqLite/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 5 + .../appsettings.Production.json | 2 + .../Api.Data.SqLite/appsettings.Staging.json | 2 + .../Api.Data.SqLite/appsettings.json | 39 + Api.Data.SqLite/Dockerfile | 35 + Api.Data.SqLite/LICENSE | 18 + Api.Data.SqLite/README.md | 167 ++++ Api.Data.SqLite/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 30 + Api.Data.SqlServer.Spatial/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 254 ++++++ Api.Data.SqlServer.Spatial/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.SqlServer.Spatial.csproj | 23 + .../Api.Data.SqlServer.Spatial.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 46 + .../Example.cs | 22 + .../Api.Data.SqlServer.Spatial.sln | 148 +++ .../Api.Data.SqlServer.Spatial.csproj | 37 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 32 + .../Data/SqlServerDbContext.cs | 10 + .../Data/SqlServerDbContextFactory.cs | 7 + .../Dockerfile.Local | 6 + .../20260415151456_Initial.Designer.cs | 658 ++++++++++++++ .../Migrations/20260415151456_Initial.cs | 546 +++++++++++ ...260415151538_AddedSpatialIndex.Designer.cs | 658 ++++++++++++++ .../20260415151538_AddedSpatialIndex.cs | 25 + .../SqlServerDbContextModelSnapshot.cs | 655 ++++++++++++++ .../Api.Data.SqlServer.Spatial/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 20 + Api.Data.SqlServer.Spatial/Dockerfile | 35 + Api.Data.SqlServer.Spatial/LICENSE | 18 + Api.Data.SqlServer.Spatial/README.md | 35 + Api.Data.SqlServer.Spatial/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Data.SqlServer/.docker/docker-compose.yml | 30 + Api.Data.SqlServer/.dockerignore | 12 + Api.Data.SqlServer/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 254 ++++++ Api.Data.SqlServer/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/autoscaler.yaml | 25 + Api.Data.SqlServer/.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 84 ++ Api.Data.SqlServer/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.SqlServer.csproj | 23 + .../Api.Data.SqlServer.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 34 + .../Api.Data.SqlServer.Models/Example.cs | 14 + .../ExampleCreatable.cs | 14 + .../ExampleCreatableAndEditable.cs | 14 + .../ExampleDeletable.cs | 14 + .../ExampleUpdatable.cs | 14 + Api.Data.SqlServer/Api.Data.SqlServer.sln | 148 +++ .../Api.Data.SqlServer.csproj | 37 + .../ExampleCreatableAndEditableController.cs | 15 + .../ExampleCreateablesController.cs | 15 + .../ExampleDeletablesController.cs | 15 + .../ExampleUpdatablesController.cs | 15 + .../Controllers/ExamplesController.cs | 15 + .../ExampleCreatableAndUpdatableMapping.cs | 26 + .../Data/Mappings/ExampleCreatableMapping.cs | 26 + .../Data/Mappings/ExampleDeletableMapping.cs | 26 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/Mappings/ExampleUpdatableMapping.cs | 26 + .../Data/SqlServerDbContext.cs | 10 + .../Data/SqlServerDbContextFactory.cs | 7 + .../Api.Data.SqlServer/Dockerfile.Local | 6 + .../20260415151054_Initial.Designer.cs | 773 ++++++++++++++++ .../Migrations/20260415151054_Initial.cs | 672 ++++++++++++++ .../SqlServerDbContextModelSnapshot.cs | 770 ++++++++++++++++ .../Api.Data.SqlServer/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.SqlServer/appsettings.json | 39 + Api.Data.SqlServer/Dockerfile | 35 + Api.Data.SqlServer/LICENSE | 18 + Api.Data.SqlServer/README.md | 223 +++++ Api.Data.SqlServer/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Data.Triggers/.docker/docker-compose.yml | 32 + Api.Data.Triggers/.dockerignore | 12 + Api.Data.Triggers/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 235 +++++ Api.Data.Triggers/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + Api.Data.Triggers/.kubernetes/autoscaler.yaml | 25 + Api.Data.Triggers/.kubernetes/configmap.yaml | 8 + Api.Data.Triggers/.kubernetes/deployment.yaml | 84 ++ Api.Data.Triggers/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Data.Triggers.csproj | 23 + .../Api.Data.Triggers.Models.csproj | 77 ++ .../Criterias/ExampleQueryCriteria.cs | 17 + .../Criterias/ExampleTriggerQueryCriteria.cs | 17 + .../Api.Data.Triggers.Models/Example.cs | 22 + .../ExampleTrigger.cs | 22 + Api.Data.Triggers/Api.Data.Triggers.sln | 148 +++ .../Api.Data.Triggers.csproj | 37 + .../Controllers/ExampleTriggersController.cs | 15 + .../Controllers/ExamplesController.cs | 15 + .../Data/Mappings/ExampleMapping.cs | 52 ++ .../Data/Mappings/ExampleTriggerMapping.cs | 28 + .../Api.Data.Triggers/Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Data/Triggers/ExampleTriggers.cs | 72 ++ .../Api.Data.Triggers/Dockerfile.Local | 6 + .../20260415151450_Initial.Designer.cs | 689 ++++++++++++++ .../Migrations/20260415151450_Initial.cs | 638 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 686 ++++++++++++++ .../Api.Data.Triggers/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Data.Triggers/appsettings.json | 16 + Api.Data.Triggers/Dockerfile | 35 + Api.Data.Triggers/LICENSE | 18 + Api.Data.Triggers/README.md | 26 + Api.Data.Triggers/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 20 + Api.Documentation.Csp/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 196 ++++ Api.Documentation.Csp/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Documentation.Csp.csproj | 23 + .../Api.Documentation.Csp.Models.csproj | 74 ++ .../Api.Documentation.Csp.sln | 133 +++ .../Api.Documentation.Csp.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.Documentation.Csp/Dockerfile.Local | 6 + .../Api.Documentation.Csp/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Documentation.Csp/appsettings.json | 29 + Api.Documentation.Csp/Dockerfile | 35 + Api.Documentation.Csp/LICENSE | 18 + Api.Documentation.Csp/README.md | 54 ++ Api.Documentation.Csp/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Documentation/.docker/docker-compose.yml | 17 + Api.Documentation/.dockerignore | 12 + Api.Documentation/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Documentation/.gitignore | 11 + Api.Documentation/.kubernetes/autoscaler.yaml | 25 + Api.Documentation/.kubernetes/configmap.yaml | 8 + Api.Documentation/.kubernetes/deployment.yaml | 78 ++ Api.Documentation/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Documentation.csproj | 23 + .../Api.Documentation.Models.csproj | 75 ++ Api.Documentation/Api.Documentation.sln | 133 +++ .../Api.Documentation.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.Documentation/Dockerfile.Local | 6 + .../Api.Documentation/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Documentation/appsettings.json | 30 + Api.Documentation/Dockerfile | 35 + Api.Documentation/LICENSE | 18 + Api.Documentation/README.md | 59 ++ Api.Documentation/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.ErrorHandling/.docker/docker-compose.yml | 17 + Api.ErrorHandling/.dockerignore | 12 + Api.ErrorHandling/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.ErrorHandling/.gitignore | 11 + Api.ErrorHandling/.kubernetes/autoscaler.yaml | 25 + Api.ErrorHandling/.kubernetes/configmap.yaml | 8 + Api.ErrorHandling/.kubernetes/deployment.yaml | 78 ++ Api.ErrorHandling/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.ErrorHandling.csproj | 23 + .../Api.ErrorHandling.Models.csproj | 75 ++ Api.ErrorHandling/Api.ErrorHandling.sln | 134 +++ .../Api.ErrorHandling.csproj | 37 + .../Controllers/ExamplesController.cs | 235 +++++ .../Api.ErrorHandling/Dockerfile.Local | 6 + .../Api.ErrorHandling/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.ErrorHandling/appsettings.json | 16 + Api.ErrorHandling/Dockerfile | 35 + Api.ErrorHandling/LICENSE | 18 + Api.ErrorHandling/README.md | 54 ++ Api.ErrorHandling/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 34 + Api.Eventing.RabbitMq/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Eventing.RabbitMq/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 90 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Eventing.RabbitMq.csproj | 23 + .../Api.Eventing.RabbitMq.Models.csproj | 77 ++ .../Api.Eventing.RabbitMq.sln | 147 +++ .../Api.Eventing.RabbitMq.csproj | 37 + .../Controllers/ExamplesController.cs | 58 ++ .../Api.Eventing.RabbitMq/Dockerfile.Local | 6 + .../Eventing/EventingHandler.cs | 19 + .../Eventing/EventingHandlerRoutingKey.cs | 24 + .../Eventing/Models/EventModel.cs | 12 + .../Eventing/Models/EventModelRoutingKey.cs | 12 + .../Api.Eventing.RabbitMq/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 8 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Eventing.RabbitMq/appsettings.json | 31 + Api.Eventing.RabbitMq/Dockerfile | 35 + Api.Eventing.RabbitMq/LICENSE | 18 + Api.Eventing.RabbitMq/README.md | 151 ++++ Api.Eventing.RabbitMq/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.HealthChecks/.docker/docker-compose.yml | 17 + Api.HealthChecks/.dockerignore | 12 + Api.HealthChecks/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 244 +++++ Api.HealthChecks/.gitignore | 11 + Api.HealthChecks/.kubernetes/autoscaler.yaml | 25 + Api.HealthChecks/.kubernetes/configmap.yaml | 8 + Api.HealthChecks/.kubernetes/deployment.yaml | 78 ++ Api.HealthChecks/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.HealthChecks.csproj | 23 + .../Api.HealthChecks.Models.csproj | 75 ++ Api.HealthChecks/Api.HealthChecks.sln | 133 +++ .../Api.HealthChecks/Api.HealthChecks.csproj | 37 + .../Controllers/ExamplesController.cs | 54 ++ .../Api.HealthChecks/Dockerfile.Local | 6 + Api.HealthChecks/Api.HealthChecks/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Api.HealthChecks/appsettings.Staging.json | 2 + .../Api.HealthChecks/appsettings.json | 15 + Api.HealthChecks/Dockerfile | 35 + Api.HealthChecks/LICENSE | 18 + Api.HealthChecks/README.md | 124 +++ Api.HealthChecks/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Hosting.Http/.docker/docker-compose.yml | 17 + Api.Hosting.Http/.dockerignore | 12 + Api.Hosting.Http/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Hosting.Http/.gitignore | 11 + Api.Hosting.Http/.kubernetes/autoscaler.yaml | 25 + Api.Hosting.Http/.kubernetes/configmap.yaml | 8 + Api.Hosting.Http/.kubernetes/deployment.yaml | 78 ++ Api.Hosting.Http/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Hosting.Http.csproj | 23 + .../Api.Hosting.Http.Models.csproj | 75 ++ Api.Hosting.Http/Api.Hosting.Http.sln | 133 +++ .../Api.Hosting.Http/Api.Hosting.Http.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.Hosting.Http/Dockerfile.Local | 6 + Api.Hosting.Http/Api.Hosting.Http/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Api.Hosting.Http/appsettings.Staging.json | 2 + .../Api.Hosting.Http/appsettings.json | 13 + Api.Hosting.Http/Dockerfile | 35 + Api.Hosting.Http/LICENSE | 18 + Api.Hosting.Http/README.md | 30 + Api.Hosting.Http/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Hosting.Https/.docker/docker-compose.yml | 20 + Api.Hosting.Https/.dockerignore | 12 + Api.Hosting.Https/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 205 +++++ Api.Hosting.Https/.gitignore | 11 + Api.Hosting.Https/.kubernetes/autoscaler.yaml | 25 + Api.Hosting.Https/.kubernetes/configmap.yaml | 8 + Api.Hosting.Https/.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/httproute-443.yaml | 18 + .../.kubernetes/httproute-80.yaml | 17 + Api.Hosting.Https/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Hosting.Https.csproj | 23 + .../Api.Hosting.Https.Models.csproj | 74 ++ Api.Hosting.Https/Api.Hosting.Https.sln | 136 +++ .../Api.Hosting.Https.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.Hosting.Https/Dockerfile.Local | 6 + .../Api.Hosting.Https/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 19 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Hosting.Https/appsettings.json | 13 + Api.Hosting.Https/Dockerfile | 35 + Api.Hosting.Https/LICENSE | 18 + Api.Hosting.Https/README.md | 94 ++ Api.Hosting.Https/icon.png | Bin 0 -> 14103 bytes Api.Hosting.Https/localhost.pfx | Bin 0 -> 2670 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.Hosting.MultipartLimits/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Hosting.MultipartLimits/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Hosting.MultipartLimits.csproj | 23 + .../Api.Hosting.MultipartLimits.Models.csproj | 75 ++ .../Api.Hosting.MultipartLimits.sln | 140 +++ .../Api.Hosting.MultipartLimits.csproj | 37 + .../Controllers/ExamplesController.cs | 37 + .../Dockerfile.Local | 6 + .../Api.Hosting.MultipartLimits/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 17 + Api.Hosting.MultipartLimits/Dockerfile | 35 + Api.Hosting.MultipartLimits/LICENSE | 18 + Api.Hosting.MultipartLimits/README.md | 47 + Api.Hosting.MultipartLimits/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Localization/.docker/docker-compose.yml | 17 + Api.Localization/.dockerignore | 12 + Api.Localization/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Localization/.gitignore | 11 + Api.Localization/.kubernetes/autoscaler.yaml | 25 + Api.Localization/.kubernetes/configmap.yaml | 8 + Api.Localization/.kubernetes/deployment.yaml | 78 ++ Api.Localization/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Localization.csproj | 23 + .../Api.Localization.Models.csproj | 75 ++ Api.Localization/Api.Localization.sln | 133 +++ .../Api.Localization/Api.Localization.csproj | 37 + .../Controllers/ExamplesController.cs | 38 + .../Api.Localization/Dockerfile.Local | 6 + Api.Localization/Api.Localization/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Api.Localization/appsettings.Staging.json | 2 + .../Api.Localization/appsettings.json | 19 + Api.Localization/Dockerfile | 35 + Api.Localization/LICENSE | 18 + Api.Localization/README.md | 52 ++ Api.Localization/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.Logging.Log4Net/.dockerignore | 12 + Api.Logging.Log4Net/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Logging.Log4Net/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ Api.Logging.Log4Net/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Logging.Log4Net.csproj | 23 + .../Api.Logging.Log4Net.Models.csproj | 77 ++ Api.Logging.Log4Net/Api.Logging.Log4Net.sln | 147 +++ .../Api.Logging.Log4Net.csproj | 37 + .../Controllers/ExamplesController.cs | 60 ++ .../Api.Logging.Log4Net/Dockerfile.Local | 6 + .../Api.Logging.Log4Net/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Logging.Log4Net/appsettings.json | 22 + Api.Logging.Log4Net/Dockerfile | 35 + Api.Logging.Log4Net/LICENSE | 18 + Api.Logging.Log4Net/README.md | 62 ++ Api.Logging.Log4Net/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.Logging.Microsoft/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Logging.Microsoft/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Logging.Microsoft.csproj | 23 + .../Api.Logging.Microsoft.Models.csproj | 77 ++ .../Api.Logging.Microsoft.sln | 147 +++ .../Api.Logging.Microsoft.csproj | 37 + .../Controllers/ExamplesController.cs | 60 ++ .../Api.Logging.Microsoft/Dockerfile.Local | 6 + .../Api.Logging.Microsoft/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Logging.Microsoft/appsettings.json | 22 + Api.Logging.Microsoft/Dockerfile | 35 + Api.Logging.Microsoft/LICENSE | 18 + Api.Logging.Microsoft/README.md | 62 ++ Api.Logging.Microsoft/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Logging.NLog/.docker/docker-compose.yml | 17 + Api.Logging.NLog/.dockerignore | 12 + Api.Logging.NLog/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Logging.NLog/.gitignore | 11 + Api.Logging.NLog/.kubernetes/autoscaler.yaml | 25 + Api.Logging.NLog/.kubernetes/configmap.yaml | 8 + Api.Logging.NLog/.kubernetes/deployment.yaml | 78 ++ Api.Logging.NLog/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Logging.NLog.csproj | 23 + .../Api.Logging.NLog.Models.csproj | 77 ++ Api.Logging.NLog/Api.Logging.NLog.sln | 147 +++ .../Api.Logging.NLog/Api.Logging.NLog.csproj | 37 + .../Controllers/ExamplesController.cs | 60 ++ .../Api.Logging.NLog/Dockerfile.Local | 6 + Api.Logging.NLog/Api.Logging.NLog/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Api.Logging.NLog/appsettings.Staging.json | 2 + .../Api.Logging.NLog/appsettings.json | 22 + Api.Logging.NLog/Dockerfile | 35 + Api.Logging.NLog/LICENSE | 18 + Api.Logging.NLog/README.md | 62 ++ Api.Logging.NLog/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.Logging.Serilog/.dockerignore | 12 + Api.Logging.Serilog/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Logging.Serilog/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ Api.Logging.Serilog/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Logging.Serilog.csproj | 23 + .../Api.Logging.Serilog.Models.csproj | 77 ++ Api.Logging.Serilog/Api.Logging.Serilog.sln | 147 +++ .../Api.Logging.Serilog.csproj | 37 + .../Controllers/ExamplesController.cs | 60 ++ .../Api.Logging.Serilog/Dockerfile.Local | 6 + .../Api.Logging.Serilog/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Logging.Serilog/appsettings.json | 22 + Api.Logging.Serilog/Dockerfile | 35 + Api.Logging.Serilog/LICENSE | 18 + Api.Logging.Serilog/README.md | 62 ++ Api.Logging.Serilog/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.MultipartJson/.docker/docker-compose.yml | 17 + Api.MultipartJson/.dockerignore | 12 + Api.MultipartJson/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.MultipartJson/.gitignore | 11 + Api.MultipartJson/.kubernetes/autoscaler.yaml | 25 + Api.MultipartJson/.kubernetes/configmap.yaml | 8 + Api.MultipartJson/.kubernetes/deployment.yaml | 78 ++ Api.MultipartJson/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.MultipartJson.csproj | 23 + .../Api.MultipartJson.Models.csproj | 75 ++ Api.MultipartJson/Api.MultipartJson.sln | 133 +++ .../Api.MultipartJson.csproj | 37 + .../Controllers/ExamplesController.cs | 42 + .../Controllers/Requests/JsonBody.cs | 15 + .../Api.MultipartJson/Dockerfile.Local | 6 + .../Api.MultipartJson/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.MultipartJson/appsettings.json | 13 + Api.MultipartJson/Dockerfile | 35 + Api.MultipartJson/LICENSE | 18 + Api.MultipartJson/README.md | 30 + Api.MultipartJson/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 20 + .../.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 205 +++++ .../.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/httproute-443.yaml | 18 + .../.kubernetes/httproute-80.yaml | 17 + .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ...PolicyHeaders.ContentSecurityPolicy.csproj | 23 + ...eaders.ContentSecurityPolicy.Models.csproj | 75 ++ ...pi.PolicyHeaders.ContentSecurityPolicy.sln | 137 +++ ...PolicyHeaders.ContentSecurityPolicy.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Dockerfile.Local | 6 + .../Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 19 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 76 ++ .../wwwroot/csp-violation.html | 17 + .../Dockerfile | 35 + .../LICENSE | 18 + .../README.md | 130 +++ .../icon.png | Bin 0 -> 14103 bytes .../localhost.pfx | Bin 0 -> 2670 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + .../.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ .../.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ...pi.PolicyHeaders.ContentTypeOptions.csproj | 23 + ...cyHeaders.ContentTypeOptions.Models.csproj | 75 ++ .../Api.PolicyHeaders.ContentTypeOptions.sln | 134 +++ ...pi.PolicyHeaders.ContentTypeOptions.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Dockerfile.Local | 6 + .../Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 18 + ...ontent-type-sniff-violation-wrong-type.txt | 1 + .../wwwroot/content-type-sniff-violation.html | 18 + .../Dockerfile | 35 + Api.PolicyHeaders.ContentTypeOptions/LICENSE | 18 + .../README.md | 41 + Api.PolicyHeaders.ContentTypeOptions/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.PolicyHeaders.Cors/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.PolicyHeaders.Cors/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.PolicyHeaders.Cors.csproj | 23 + .../Api.PolicyHeaders.Cors.Models.csproj | 75 ++ .../Api.PolicyHeaders.Cors.sln | 134 +++ .../Api.PolicyHeaders.Cors.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.PolicyHeaders.Cors/Dockerfile.Local | 6 + .../Api.PolicyHeaders.Cors/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.PolicyHeaders.Cors/appsettings.json | 36 + ...s-violation-credetntials-not-alloweed.html | 19 + .../cors-violation-header-not-allowed.html | 22 + .../cors-violation-method-not-allowed.html | 19 + .../cors-violation-origin-not-allowed.html | 17 + .../cors-violation-preflight-blocked.html | 23 + Api.PolicyHeaders.Cors/Dockerfile | 35 + Api.PolicyHeaders.Cors/LICENSE | 18 + Api.PolicyHeaders.Cors/README.md | 54 ++ Api.PolicyHeaders.Cors/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + .../.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.PolicyHeaders.ForwardedHeaders/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ....Api.PolicyHeaders.ForwardedHeaders.csproj | 23 + ...licyHeaders.ForwardedHeaders.Models.csproj | 75 ++ .../Api.PolicyHeaders.ForwardedHeaders.sln | 134 +++ .../Api.PolicyHeaders.ForwardedHeaders.csproj | 37 + .../Controllers/ExamplesController.cs | 40 + .../Dockerfile.Local | 6 + .../Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 19 + Api.PolicyHeaders.ForwardedHeaders/Dockerfile | 35 + Api.PolicyHeaders.ForwardedHeaders/LICENSE | 18 + Api.PolicyHeaders.ForwardedHeaders/README.md | 42 + Api.PolicyHeaders.ForwardedHeaders/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.PolicyHeaders.FrameOptions/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.PolicyHeaders.FrameOptions/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ...ests.Api.PolicyHeaders.FrameOptions.csproj | 23 + ...i.PolicyHeaders.FrameOptions.Models.csproj | 75 ++ .../Api.PolicyHeaders.FrameOptions.sln | 134 +++ .../Api.PolicyHeaders.FrameOptions.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Dockerfile.Local | 6 + .../Api.PolicyHeaders.FrameOptions/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 18 + .../wwwroot/frame-options-violation.html | 27 + Api.PolicyHeaders.FrameOptions/Dockerfile | 35 + Api.PolicyHeaders.FrameOptions/LICENSE | 18 + Api.PolicyHeaders.FrameOptions/README.md | 40 + Api.PolicyHeaders.FrameOptions/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 20 + Api.PolicyHeaders.Hsts/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 205 +++++ Api.PolicyHeaders.Hsts/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/httproute-443.yaml | 18 + .../.kubernetes/httproute-80.yaml | 17 + .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.PolicyHeaders.Hsts.csproj | 23 + .../Api.PolicyHeaders.Hsts.Models.csproj | 75 ++ .../Api.PolicyHeaders.Hsts.sln | 137 +++ .../Api.PolicyHeaders.Hsts.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.PolicyHeaders.Hsts/Dockerfile.Local | 6 + .../Api.PolicyHeaders.Hsts/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 15 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.PolicyHeaders.Hsts/appsettings.json | 20 + .../wwwroot/hsts-violation-image.png | Bin 0 -> 15262 bytes .../wwwroot/hsts-violation.html | 41 + Api.PolicyHeaders.Hsts/Dockerfile | 35 + Api.PolicyHeaders.Hsts/LICENSE | 18 + Api.PolicyHeaders.Hsts/README.md | 43 + Api.PolicyHeaders.Hsts/icon.png | Bin 0 -> 14103 bytes Api.PolicyHeaders.Hsts/localhost.pfx | Bin 0 -> 2670 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + .../.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.PolicyHeaders.ReferrerPolicy/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ...ts.Api.PolicyHeaders.ReferrerPolicy.csproj | 23 + ...PolicyHeaders.ReferrerPolicy.Models.csproj | 75 ++ .../Api.PolicyHeaders.ReferrerPolicy.sln | 134 +++ .../Api.PolicyHeaders.ReferrerPolicy.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Dockerfile.Local | 6 + .../Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 18 + .../wwwroot/referrer-policy-violation.html | 55 ++ Api.PolicyHeaders.ReferrerPolicy/Dockerfile | 35 + Api.PolicyHeaders.ReferrerPolicy/LICENSE | 18 + Api.PolicyHeaders.ReferrerPolicy/README.md | 41 + Api.PolicyHeaders.ReferrerPolicy/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.PolicyHeaders.Robots/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.PolicyHeaders.Robots/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.PolicyHeaders.Robots.csproj | 23 + .../Api.PolicyHeaders.Robots.Models.csproj | 75 ++ .../Api.PolicyHeaders.Robots.sln | 134 +++ .../Api.PolicyHeaders.Robots.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.PolicyHeaders.Robots/Dockerfile.Local | 6 + .../Api.PolicyHeaders.Robots/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.PolicyHeaders.Robots/appsettings.json | 24 + Api.PolicyHeaders.Robots/Dockerfile | 35 + Api.PolicyHeaders.Robots/LICENSE | 18 + Api.PolicyHeaders.Robots/README.md | 48 + Api.PolicyHeaders.Robots/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.PolicyHeaders.XssProtection/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.PolicyHeaders.XssProtection/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + ...sts.Api.PolicyHeaders.XssProtection.csproj | 23 + ....PolicyHeaders.XssProtection.Models.csproj | 75 ++ .../Api.PolicyHeaders.XssProtection.sln | 134 +++ .../Api.PolicyHeaders.XssProtection.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Dockerfile.Local | 6 + .../Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 19 + .../wwwroot/xss-violation.html | 22 + Api.PolicyHeaders.XssProtection/Dockerfile | 35 + Api.PolicyHeaders.XssProtection/LICENSE | 18 + Api.PolicyHeaders.XssProtection/README.md | 49 + Api.PolicyHeaders.XssProtection/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.RequestTracing/.docker/docker-compose.yml | 17 + Api.RequestTracing/.dockerignore | 12 + Api.RequestTracing/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.RequestTracing/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + Api.RequestTracing/.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ Api.RequestTracing/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.RequestTracing.csproj | 23 + .../Api.RequestTracing.Models.csproj | 75 ++ Api.RequestTracing/Api.RequestTracing.sln | 133 +++ .../Api.RequestTracing.csproj | 37 + .../Controllers/ExamplesController.cs | 34 + .../Api.RequestTracing/Dockerfile.Local | 6 + .../Api.RequestTracing/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.RequestTracing/appsettings.json | 13 + Api.RequestTracing/Dockerfile | 35 + Api.RequestTracing/LICENSE | 18 + Api.RequestTracing/README.md | 31 + Api.RequestTracing/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.ResponseCache/.docker/docker-compose.yml | 17 + Api.ResponseCache/.dockerignore | 12 + Api.ResponseCache/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.ResponseCache/.gitignore | 11 + Api.ResponseCache/.kubernetes/autoscaler.yaml | 25 + Api.ResponseCache/.kubernetes/configmap.yaml | 8 + Api.ResponseCache/.kubernetes/deployment.yaml | 78 ++ Api.ResponseCache/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.ResponseCache.csproj | 23 + .../Api.ResponseCache.Models.csproj | 75 ++ Api.ResponseCache/Api.ResponseCache.sln | 133 +++ .../Api.ResponseCache.csproj | 37 + .../Controllers/ExamplesController.cs | 48 + .../Api.ResponseCache/Dockerfile.Local | 6 + .../Api.ResponseCache/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.ResponseCache/appsettings.json | 18 + Api.ResponseCache/Dockerfile | 35 + Api.ResponseCache/LICENSE | 18 + Api.ResponseCache/README.md | 43 + Api.ResponseCache/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + .../.docker/docker-compose.yml | 17 + Api.ResponseCompression/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.ResponseCompression/.gitignore | 11 + .../.kubernetes/autoscaler.yaml | 25 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/deployment.yaml | 78 ++ .../.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.ResponseCompression.csproj | 23 + .../Api.ResponseCompression.Models.csproj | 75 ++ .../Api.ResponseCompression.sln | 133 +++ .../Api.ResponseCompression.csproj | 37 + .../Controllers/ExamplesController.cs | 36 + .../Api.ResponseCompression/Dockerfile.Local | 6 + .../Api.ResponseCompression/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.ResponseCompression/appsettings.json | 17 + Api.ResponseCompression/Dockerfile | 35 + Api.ResponseCompression/LICENSE | 18 + Api.ResponseCompression/README.md | 42 + Api.ResponseCompression/icon.png | Bin 0 -> 14103 bytes Api.Session/.docker/docker-compose.dcproj | 13 + Api.Session/.docker/docker-compose.yml | 17 + Api.Session/.dockerignore | 12 + Api.Session/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Session/.gitignore | 11 + Api.Session/.kubernetes/autoscaler.yaml | 25 + Api.Session/.kubernetes/configmap.yaml | 8 + Api.Session/.kubernetes/deployment.yaml | 78 ++ Api.Session/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Session.csproj | 23 + .../Api.Session.Models.csproj | 75 ++ Api.Session/Api.Session.sln | 133 +++ Api.Session/Api.Session/Api.Session.csproj | 37 + .../Controllers/ExamplesController.cs | 82 ++ Api.Session/Api.Session/Dockerfile.Local | 6 + Api.Session/Api.Session/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Api.Session/appsettings.Development.json | 2 + .../Api.Session/appsettings.Production.json | 2 + .../Api.Session/appsettings.Staging.json | 2 + Api.Session/Api.Session/appsettings.json | 16 + Api.Session/Dockerfile | 35 + Api.Session/LICENSE | 18 + Api.Session/README.md | 42 + Api.Session/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.StartupTasks/.docker/docker-compose.yml | 17 + Api.StartupTasks/.dockerignore | 12 + Api.StartupTasks/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.StartupTasks/.gitignore | 11 + Api.StartupTasks/.kubernetes/autoscaler.yaml | 25 + Api.StartupTasks/.kubernetes/configmap.yaml | 8 + Api.StartupTasks/.kubernetes/deployment.yaml | 78 ++ Api.StartupTasks/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.StartupTasks.csproj | 23 + .../Api.StartupTasks.Models.csproj | 75 ++ Api.StartupTasks/Api.StartupTasks.sln | 133 +++ .../Api.StartupTasks/Api.StartupTasks.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.StartupTasks/Dockerfile.Local | 6 + Api.StartupTasks/Api.StartupTasks/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Startup/ExampleStartupTask.cs | 30 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Api.StartupTasks/appsettings.Staging.json | 2 + .../Api.StartupTasks/appsettings.json | 15 + Api.StartupTasks/Dockerfile | 35 + Api.StartupTasks/LICENSE | 18 + Api.StartupTasks/README.md | 42 + Api.StartupTasks/icon.png | Bin 0 -> 14103 bytes Api.StaticFiles/.docker/docker-compose.dcproj | 13 + Api.StaticFiles/.docker/docker-compose.yml | 17 + Api.StaticFiles/.dockerignore | 12 + Api.StaticFiles/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.StaticFiles/.gitignore | 11 + Api.StaticFiles/.kubernetes/autoscaler.yaml | 25 + Api.StaticFiles/.kubernetes/configmap.yaml | 8 + Api.StaticFiles/.kubernetes/deployment.yaml | 78 ++ Api.StaticFiles/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.StaticFiles.csproj | 23 + .../Api.StaticFiles.Models.csproj | 75 ++ Api.StaticFiles/Api.StaticFiles.sln | 133 +++ .../Api.StaticFiles/Api.StaticFiles.csproj | 37 + .../Controllers/ExamplesController.cs | 31 + .../Api.StaticFiles/Dockerfile.Local | 6 + Api.StaticFiles/Api.StaticFiles/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Api.StaticFiles/appsettings.Staging.json | 2 + .../Api.StaticFiles/appsettings.json | 13 + .../fonts/open-sans-v44-latin-regular.woff2 | Bin 0 -> 18640 bytes .../Api.StaticFiles/wwwroot/images/image.jpg | Bin 0 -> 256546 bytes .../Api.StaticFiles/wwwroot/index.html | 22 + Api.StaticFiles/Dockerfile | 35 + Api.StaticFiles/LICENSE | 18 + Api.StaticFiles/README.md | 31 + Api.StaticFiles/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Storage.Azure/.docker/docker-compose.yml | 19 + Api.Storage.Azure/.dockerignore | 12 + Api.Storage.Azure/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 237 +++++ Api.Storage.Azure/.gitignore | 11 + Api.Storage.Azure/.kubernetes/autoscaler.yaml | 25 + Api.Storage.Azure/.kubernetes/configmap.yaml | 9 + Api.Storage.Azure/.kubernetes/deployment.yaml | 94 ++ Api.Storage.Azure/.kubernetes/service.yaml | 12 + Api.Storage.Azure/.kubernetes/storage-pv.yaml | 17 + .../.kubernetes/storage-pvc.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Storage.Azure.csproj | 23 + .../Api.Storage.Azure.Models.csproj | 77 ++ Api.Storage.Azure/Api.Storage.Azure.sln | 149 +++ .../Api.Storage.Azure.csproj | 38 + .../Controllers/ExamplesController.cs | 44 + .../Api.Storage.Azure/Dockerfile.Local | 6 + .../Api.Storage.Azure/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Storage.Azure/appsettings.json | 22 + Api.Storage.Azure/Dockerfile | 35 + Api.Storage.Azure/LICENSE | 18 + Api.Storage.Azure/README.md | 165 ++++ Api.Storage.Azure/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 13 + Api.Storage.Local/.docker/docker-compose.yml | 19 + Api.Storage.Local/.dockerignore | 12 + Api.Storage.Local/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 194 ++++ Api.Storage.Local/.gitignore | 11 + Api.Storage.Local/.kubernetes/autoscaler.yaml | 25 + Api.Storage.Local/.kubernetes/configmap.yaml | 8 + Api.Storage.Local/.kubernetes/deployment.yaml | 89 ++ Api.Storage.Local/.kubernetes/service.yaml | 12 + .../.kubernetes/storage-pvc.yaml | 12 + .../.kubernetes/storage-storageclass.yaml | 10 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Storage.Local.csproj | 23 + .../Api.Storage.Local.Models.csproj | 77 ++ Api.Storage.Local/Api.Storage.Local.sln | 149 +++ .../Api.Storage.Local.csproj | 38 + .../Controllers/ExamplesController.cs | 44 + .../Api.Storage.Local/Dockerfile.Local | 6 + .../Api.Storage.Local/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Api.Storage.Local/appsettings.json | 21 + Api.Storage.Local/Dockerfile | 35 + Api.Storage.Local/LICENSE | 18 + Api.Storage.Local/README.md | 123 +++ Api.Storage.Local/icon.png | Bin 0 -> 14103 bytes Api.TimeZone/.docker/docker-compose.dcproj | 13 + Api.TimeZone/.docker/docker-compose.yml | 17 + Api.TimeZone/.dockerignore | 12 + Api.TimeZone/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.TimeZone/.gitignore | 11 + Api.TimeZone/.kubernetes/autoscaler.yaml | 25 + Api.TimeZone/.kubernetes/configmap.yaml | 8 + Api.TimeZone/.kubernetes/deployment.yaml | 78 ++ Api.TimeZone/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.TimeZone.csproj | 23 + .../Api.TimeZone.Models.csproj | 75 ++ Api.TimeZone/Api.TimeZone.sln | 133 +++ Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj | 37 + .../Controllers/ExamplesController.cs | 71 ++ .../Controllers/Requests/DateTimeRequest.cs | 16 + Api.TimeZone/Api.TimeZone/Dockerfile.Local | 6 + Api.TimeZone/Api.TimeZone/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Api.TimeZone/appsettings.Development.json | 2 + .../Api.TimeZone/appsettings.Production.json | 2 + .../Api.TimeZone/appsettings.Staging.json | 2 + Api.TimeZone/Api.TimeZone/appsettings.json | 16 + Api.TimeZone/Dockerfile | 35 + Api.TimeZone/LICENSE | 18 + Api.TimeZone/README.md | 52 ++ Api.TimeZone/icon.png | Bin 0 -> 14103 bytes Api.Versioning/.docker/docker-compose.dcproj | 13 + Api.Versioning/.docker/docker-compose.yml | 17 + Api.Versioning/.dockerignore | 12 + Api.Versioning/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.Versioning/.gitignore | 11 + Api.Versioning/.kubernetes/autoscaler.yaml | 25 + Api.Versioning/.kubernetes/configmap.yaml | 8 + Api.Versioning/.kubernetes/deployment.yaml | 78 ++ Api.Versioning/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Versioning.csproj | 23 + .../Api.Versioning.Models.csproj | 75 ++ Api.Versioning/Api.Versioning.sln | 133 +++ .../Api.Versioning/Api.Versioning.csproj | 37 + .../Controllers/ExamplesController.cs | 52 ++ .../Api.Versioning/Dockerfile.Local | 6 + Api.Versioning/Api.Versioning/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Api.Versioning/appsettings.Staging.json | 2 + .../Api.Versioning/appsettings.json | 13 + Api.Versioning/Dockerfile | 35 + Api.Versioning/LICENSE | 18 + Api.Versioning/README.md | 35 + Api.Versioning/icon.png | Bin 0 -> 14103 bytes Api.VirusScan/.docker/docker-compose.dcproj | 13 + Api.VirusScan/.docker/docker-compose.yml | 27 + Api.VirusScan/.dockerignore | 12 + Api.VirusScan/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api.VirusScan/.gitignore | 11 + Api.VirusScan/.kubernetes/autoscaler.yaml | 25 + Api.VirusScan/.kubernetes/configmap.yaml | 8 + Api.VirusScan/.kubernetes/deployment.yaml | 78 ++ Api.VirusScan/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.VirusScan.csproj | 23 + .../Api.VirusScan.Models.csproj | 75 ++ Api.VirusScan/Api.VirusScan.sln | 140 +++ .../Api.VirusScan/Api.VirusScan.csproj | 37 + .../Controllers/ExamplesController.cs | 36 + Api.VirusScan/Api.VirusScan/Dockerfile.Local | 6 + Api.VirusScan/Api.VirusScan/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../Api.VirusScan/appsettings.Production.json | 2 + .../Api.VirusScan/appsettings.Staging.json | 2 + Api.VirusScan/Api.VirusScan/appsettings.json | 22 + Api.VirusScan/Dockerfile | 35 + Api.VirusScan/LICENSE | 18 + Api.VirusScan/README.md | 71 ++ Api.VirusScan/icon.png | Bin 0 -> 14103 bytes Api._Blank/.docker/docker-compose.dcproj | 13 + Api._Blank/.docker/docker-compose.yml | 17 + Api._Blank/.dockerignore | 12 + Api._Blank/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Api._Blank/.gitignore | 11 + Api._Blank/.kubernetes/autoscaler.yaml | 25 + Api._Blank/.kubernetes/configmap.yaml | 8 + Api._Blank/.kubernetes/deployment.yaml | 78 ++ Api._Blank/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Api.Blank/Tests.Api.Blank.csproj | 23 + .../Api.Blank.Models/Api.Blank.Models.csproj | 75 ++ Api._Blank/Api.Blank.sln | 133 +++ Api._Blank/Api.Blank/Api.Blank.csproj | 37 + Api._Blank/Api.Blank/Dockerfile.Local | 6 + Api._Blank/Api.Blank/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Api.Blank/appsettings.Development.json | 2 + .../Api.Blank/appsettings.Production.json | 2 + Api._Blank/Api.Blank/appsettings.Staging.json | 2 + Api._Blank/Api.Blank/appsettings.json | 13 + Api._Blank/Dockerfile | 35 + Api._Blank/LICENSE | 18 + Api._Blank/README.md | 26 + Api._Blank/icon.png | Bin 0 -> 14103 bytes .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.CustomConfigSection/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.CustomConfigSection/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.CustomConfigSection.csproj | 23 + .../Console.CustomConfigSection.sln | 123 +++ .../Config/CustomOptions.cs | 17 + .../Console.CustomConfigSection.csproj | 43 + .../Dockerfile.Local | 5 + .../Console.CustomConfigSection/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 35 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 8 + Console.CustomConfigSection/Dockerfile | 31 + Console.CustomConfigSection/README.md | 59 ++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.CustomService/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.CustomService/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.CustomService.csproj | 23 + .../Console.CustomService.sln | 123 +++ .../Console.CustomService.csproj | 43 + .../Console.CustomService/Dockerfile.Local | 5 + .../Console.CustomService/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../Services/Abstractions/IExampleService.cs | 15 + .../Services/ExampleService.cs | 16 + .../Workers/ExampleWorker.cs | 35 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.CustomService/appsettings.json | 5 + Console.CustomService/Dockerfile | 31 + Console.CustomService/README.md | 38 + .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.Data.InMemory/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.Data.InMemory/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Data.InMemory.csproj | 23 + .../Console.Data.InMemory.sln | 137 +++ .../Console.Data.InMemory.csproj | 45 + .../Data/InMemoryDbContext.cs | 10 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/Models/Example.cs | 14 + .../Console.Data.InMemory/Dockerfile.Local | 5 + .../Console.Data.InMemory/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 45 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Data.InMemory/appsettings.json | 27 + Console.Data.InMemory/Dockerfile | 31 + Console.Data.InMemory/README.md | 71 ++ .../.docker/docker-compose.dcproj | 15 + Console.Data.MySql/.docker/docker-compose.yml | 29 + Console.Data.MySql/.dockerignore | 12 + Console.Data.MySql/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 207 +++++ Console.Data.MySql/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + Console.Data.MySql/.kubernetes/configmap.yaml | 8 + Console.Data.MySql/.kubernetes/cronjob.yaml | 57 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Data.MySql.csproj | 23 + Console.Data.MySql/Console.Data.MySql.sln | 138 +++ .../Console.Data.MySql.csproj | 49 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Console.Data.MySql/Data/Models/Example.cs | 14 + .../Console.Data.MySql/Data/MySqlDbContext.cs | 10 + .../Data/MySqlDbContextFactory.cs | 7 + .../Console.Data.MySql/Dockerfile.Local | 5 + .../20260425173327_Initial.Designer.cs | 651 ++++++++++++++ .../Migrations/20260425173327_Initial.cs | 601 +++++++++++++ .../Migrations/MySqlDbContextModelSnapshot.cs | 648 +++++++++++++ .../Console.Data.MySql/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 45 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Data.MySql/appsettings.json | 24 + Console.Data.MySql/Dockerfile | 31 + Console.Data.MySql/README.md | 174 ++++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 27 + Console.Data.PostgreSQL/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 215 +++++ Console.Data.PostgreSQL/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 57 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Data.PostgreSQL.csproj | 23 + .../Console.Data.PostgreSQL.sln | 138 +++ .../Console.Data.PostgreSQL.csproj | 49 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/Models/Example.cs | 14 + .../Data/PostgreSqlDbContext.cs | 10 + .../Data/PostgreSqlDbContextFactory.cs | 7 + .../Console.Data.PostgreSQL/Dockerfile.Local | 5 + .../20260425181509_Initial.Designer.cs | 650 ++++++++++++++ .../Migrations/20260425181509_Initial.cs | 544 +++++++++++ .../PostgreSqlDbContextModelSnapshot.cs | 647 +++++++++++++ .../Console.Data.PostgreSQL/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 45 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Data.PostgreSQL/appsettings.json | 24 + Console.Data.PostgreSQL/Dockerfile | 31 + Console.Data.PostgreSQL/README.md | 186 ++++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 16 + Console.Data.SqLite/.dockerignore | 12 + Console.Data.SqLite/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 181 ++++ Console.Data.SqLite/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + Console.Data.SqLite/.kubernetes/cronjob.yaml | 58 ++ Console.Data.SqLite/.kubernetes/data-pvc.yaml | 11 + .../.kubernetes/data-storageclass.yaml | 10 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Data.SqLite.csproj | 23 + Console.Data.SqLite/Console.Data.SqLite.sln | 139 +++ .../Console.Data.SqLite.csproj | 45 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/Models/Example.cs | 14 + .../Data/SqLiteDbContext.cs | 10 + .../Data/SqLiteDbContextFactory.cs | 7 + .../Console.Data.SqLite/Dockerfile.Local | 5 + .../20260427110613_Initial.Designer.cs | 638 +++++++++++++ .../Migrations/20260427110613_Initial.cs | 540 +++++++++++ .../SqLiteDbContextModelSnapshot.cs | 635 +++++++++++++ .../Console.Data.SqLite/Program.cs | 15 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 45 + .../appsettings.Development.json | 5 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Data.SqLite/appsettings.json | 24 + Console.Data.SqLite/Dockerfile | 31 + Console.Data.SqLite/README.md | 151 ++++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 27 + Console.Data.SqlServer/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 227 +++++ Console.Data.SqlServer/.gitignore | 11 + .../.kubernetes/auth-sql-secret.yaml | 9 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 57 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Data.SqlServer.csproj | 23 + .../Console.Data.SqlServer.sln | 138 +++ .../Console.Data.SqlServer.csproj | 45 + .../Data/Mappings/ExampleMapping.cs | 26 + .../Data/Models/Example.cs | 14 + .../Data/SqlServerDbContext.cs | 10 + .../Data/SqlServerDbContextFactory.cs | 7 + .../Console.Data.SqlServer/Dockerfile.Local | 5 + .../20260425181618_Initial.Designer.cs | 653 ++++++++++++++ .../Migrations/20260425181618_Initial.cs | 544 +++++++++++ .../SqlServerDbContextModelSnapshot.cs | 650 ++++++++++++++ .../Console.Data.SqlServer/Program.cs | 13 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 45 + .../appsettings.Development.json | 6 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Data.SqlServer/appsettings.json | 24 + Console.Data.SqlServer/Dockerfile | 31 + Console.Data.SqlServer/README.md | 198 ++++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 31 + Console.Eventing.RabbitMq/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.Eventing.RabbitMq/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 62 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Eventing.RabbitMq.csproj | 23 + .../Console.Eventing.RabbitMq.sln | 137 +++ .../Console.Eventing.RabbitMq.csproj | 49 + .../Dockerfile.Local | 5 + .../Eventing/EventingHandler.cs | 26 + .../Eventing/Models/EventModel.cs | 12 + .../Console.Eventing.RabbitMq/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 36 + .../appsettings.Development.json | 8 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 18 + Console.Eventing.RabbitMq/Dockerfile | 31 + Console.Eventing.RabbitMq/README.md | 126 +++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.ExceptionHandling/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 150 ++++ Console.ExceptionHandling/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.ExceptionHandling.csproj | 23 + .../Console.ExceptionHandling.sln | 123 +++ .../Console.ExceptionHandling.csproj | 43 + .../Dockerfile.Local | 5 + .../Console.ExceptionHandling/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 28 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 5 + Console.ExceptionHandling/Dockerfile | 31 + Console.ExceptionHandling/README.md | 23 + .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.Localization/.dockerignore | 12 + Console.Localization/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.Localization/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + Console.Localization/.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Localization.csproj | 23 + Console.Localization/Console.Localization.sln | 123 +++ .../Console.Localization.csproj | 43 + .../Console.Localization/Dockerfile.Local | 5 + .../Console.Localization/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 36 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Localization/appsettings.json | 8 + Console.Localization/Dockerfile | 31 + Console.Localization/README.md | 32 + .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.Logging.Log4Net/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.Logging.Log4Net/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Logging.Log4Net.csproj | 23 + .../Console.Logging.Log4Net.sln | 137 +++ .../Console.Logging.Log4Net.csproj | 45 + .../Console.Logging.Log4Net/Dockerfile.Local | 5 + .../Console.Logging.Log4Net/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 30 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Logging.Log4Net/appsettings.json | 14 + Console.Logging.Log4Net/Dockerfile | 31 + Console.Logging.Log4Net/README.md | 54 ++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.Logging.Microsoft/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.Logging.Microsoft/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Logging.Microsoft.csproj | 23 + .../Console.Logging.Microsoft.sln | 137 +++ .../Console.Logging.Microsoft.csproj | 45 + .../Dockerfile.Local | 5 + .../Console.Logging.Microsoft/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 30 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../appsettings.json | 14 + Console.Logging.Microsoft/Dockerfile | 31 + Console.Logging.Microsoft/README.md | 54 ++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.Logging.NLog/.dockerignore | 12 + Console.Logging.NLog/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.Logging.NLog/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + Console.Logging.NLog/.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Logging.NLog.csproj | 23 + Console.Logging.NLog/Console.Logging.NLog.sln | 137 +++ .../Console.Logging.NLog.csproj | 45 + .../Console.Logging.NLog/Dockerfile.Local | 5 + .../Console.Logging.NLog/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 30 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Logging.NLog/appsettings.json | 14 + Console.Logging.NLog/Dockerfile | 31 + Console.Logging.NLog/README.md | 54 ++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.Logging.Serilog/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.Logging.Serilog/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Logging.Serilog.csproj | 23 + .../Console.Logging.Serilog.sln | 137 +++ .../Console.Logging.Serilog.csproj | 45 + .../Console.Logging.Serilog/Dockerfile.Local | 5 + .../Console.Logging.Serilog/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 30 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Logging.Serilog/appsettings.json | 14 + Console.Logging.Serilog/Dockerfile | 31 + Console.Logging.Serilog/README.md | 54 ++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 14 + Console.StartupTasks/.dockerignore | 12 + Console.StartupTasks/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.StartupTasks/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + Console.StartupTasks/.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.StartupTasks.csproj | 23 + Console.StartupTasks/Console.StartupTasks.sln | 123 +++ .../Console.StartupTasks.csproj | 43 + .../Console.StartupTasks/Dockerfile.Local | 5 + .../Console.StartupTasks/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Startup/ExampleStartupTask.cs | 30 + .../Workers/ExampleWorker.cs | 30 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.StartupTasks/appsettings.json | 5 + Console.StartupTasks/Dockerfile | 31 + Console.StartupTasks/README.md | 24 + .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 16 + Console.Storage.Azure/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 209 +++++ Console.Storage.Azure/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 64 ++ .../.kubernetes/storage-pv.yaml | 17 + .../.kubernetes/storage-pvc.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Storage.Azure.csproj | 23 + .../Console.Storage.Azure.sln | 139 +++ .../Console.Storage.Azure.csproj | 45 + .../Console.Storage.Azure/Dockerfile.Local | 5 + .../Console.Storage.Azure/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 34 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Storage.Azure/appsettings.json | 8 + Console.Storage.Azure/Dockerfile | 31 + Console.Storage.Azure/README.md | 134 +++ .../.docker/docker-compose.dcproj | 15 + .../.docker/docker-compose.yml | 16 + Console.Storage.Local/.dockerignore | 12 + .../.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 167 ++++ Console.Storage.Local/.gitignore | 11 + .../.kubernetes/configmap.yaml | 8 + .../.kubernetes/cronjob.yaml | 63 ++ .../.kubernetes/storage-pvc.yaml | 11 + .../.kubernetes/storage-storageclass.yaml | 10 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Storage.Local.csproj | 23 + .../Console.Storage.Local.sln | 140 +++ .../Console.Storage.Local.csproj | 45 + .../Console.Storage.Local/Dockerfile.Local | 5 + .../Console.Storage.Local/Program.cs | 12 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/ExampleWorker.cs | 34 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../appsettings.Staging.json | 2 + .../Console.Storage.Local/appsettings.json | 8 + Console.Storage.Local/Dockerfile | 31 + Console.Storage.Local/README.md | 99 ++ Console.Workers/.docker/docker-compose.dcproj | 15 + Console.Workers/.docker/docker-compose.yml | 14 + Console.Workers/.dockerignore | 12 + Console.Workers/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console.Workers/.gitignore | 11 + Console.Workers/.kubernetes/configmap.yaml | 8 + Console.Workers/.kubernetes/cronjob.yaml | 53 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Workers.csproj | 23 + Console.Workers/Console.Workers.sln | 123 +++ .../Console.Workers/Console.Workers.csproj | 43 + .../Console.Workers/Dockerfile.Local | 5 + Console.Workers/Console.Workers/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../Workers/AnotherExampleWorker.cs | 27 + .../Console.Workers/Workers/ExampleWorker.cs | 27 + .../appsettings.Development.json | 2 + .../appsettings.Production.json | 2 + .../Console.Workers/appsettings.Staging.json | 2 + .../Console.Workers/appsettings.json | 5 + Console.Workers/Dockerfile | 31 + Console.Workers/README.md | 23 + Console._Blank/.docker/docker-compose.dcproj | 15 + Console._Blank/.docker/docker-compose.yml | 14 + Console._Blank/.dockerignore | 12 + Console._Blank/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 151 ++++ Console._Blank/.gitignore | 11 + Console._Blank/.kubernetes/configmap.yaml | 8 + Console._Blank/.kubernetes/cronjob.yaml | 51 ++ .../Properties/DoNotParallelize.cs | 3 + .../Tests.Console.Blank.csproj | 23 + Console._Blank/Console.Blank.sln | 123 +++ .../Console.Blank/Console.Blank.csproj | 43 + Console._Blank/Console.Blank/Dockerfile.Local | 5 + Console._Blank/Console.Blank/Program.cs | 10 + .../Properties/InternalsVisibleTo.cs | 3 + .../appsettings.Development.json | 2 + .../Console.Blank/appsettings.Production.json | 2 + .../Console.Blank/appsettings.Staging.json | 2 + Console._Blank/Console.Blank/appsettings.json | 5 + Console._Blank/Dockerfile | 31 + Console._Blank/README.md | 26 + Nano.Lessons.sln.cmd | 2 + Web._Blank/.docker/docker-compose.dcproj | 13 + Web._Blank/.docker/docker-compose.yml | 17 + Web._Blank/.dockerignore | 12 + Web._Blank/.github/config/slack.yml | 18 + .../.github/workflows/build-and-deploy.yml | 179 ++++ Web._Blank/.gitignore | 11 + Web._Blank/.kubernetes/autoscaler.yaml | 25 + Web._Blank/.kubernetes/configmap.yaml | 8 + Web._Blank/.kubernetes/deployment.yaml | 78 ++ Web._Blank/.kubernetes/service.yaml | 12 + .../Properties/DoNotParallelize.cs | 3 + .../Tests.Web.Blank/Tests.Web.Blank.csproj | 23 + Web._Blank/Dockerfile | 35 + Web._Blank/LICENSE | 18 + Web._Blank/README.md | 28 + .../Web.Blank.Models/Web.Blank.Models.csproj | 75 ++ Web._Blank/Web.Blank.sln | 140 +++ Web._Blank/Web.Blank/App.razor | 11 + Web._Blank/Web.Blank/Dockerfile.Local | 6 + Web._Blank/Web.Blank/Program.cs | 11 + .../Properties/InternalsVisibleTo.cs | 3 + Web._Blank/Web.Blank/Web.Blank.csproj | 36 + Web._Blank/Web.Blank/_Imports.razor | 2 + .../Web.Blank/appsettings.Development.json | 2 + .../Web.Blank/appsettings.Production.json | 2 + Web._Blank/Web.Blank/appsettings.Staging.json | 2 + Web._Blank/Web.Blank/appsettings.json | 13 + Web._Blank/icon.png | Bin 0 -> 14103 bytes 2859 files changed, 146437 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/workflows/build-and-deploy.yml create mode 100644 .gitignore create mode 100644 Api.ApiClients.Audit/.docker/docker-compose.dcproj create mode 100644 Api.ApiClients.Audit/.docker/docker-compose.yml create mode 100644 Api.ApiClients.Audit/.dockerignore create mode 100644 Api.ApiClients.Audit/.github/config/slack.yml create mode 100644 Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml create mode 100644 Api.ApiClients.Audit/.gitignore create mode 100644 Api.ApiClients.Audit/.kubernetes/autoscaler.yaml create mode 100644 Api.ApiClients.Audit/.kubernetes/configmap.yaml create mode 100644 Api.ApiClients.Audit/.kubernetes/deployment.yaml create mode 100644 Api.ApiClients.Audit/.kubernetes/service.yaml create mode 100644 Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Properties/DoNotParallelize.cs create mode 100644 Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Tests.Api.ApiClients.Audit.csproj create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ApiClient/NanoApiClient.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Example.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ExampleNavigation.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Api.ApiClients.Audit.Service.csproj create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/AuditController.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/ExamplesController.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleMapping.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleNavigationMapping.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContext.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContextFactory.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.Designer.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Program.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Properties/InternalsVisibleTo.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Development.json create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Production.json create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Staging.json create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.json create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit.sln create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/Api.ApiClients.Audit.csproj create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/Controllers/AuditController.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/Controllers/ExamplesController.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/Dockerfile.Local create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/Program.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/Properties/InternalsVisibleTo.cs create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Development.json create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Production.json create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Staging.json create mode 100644 Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.json create mode 100644 Api.ApiClients.Audit/Dockerfile create mode 100644 Api.ApiClients.Audit/LICENSE create mode 100644 Api.ApiClients.Audit/README.md create mode 100644 Api.ApiClients.Audit/icon.png create mode 100644 Api.ApiClients.Entity/.docker/docker-compose.dcproj create mode 100644 Api.ApiClients.Entity/.docker/docker-compose.yml create mode 100644 Api.ApiClients.Entity/.dockerignore create mode 100644 Api.ApiClients.Entity/.github/config/slack.yml create mode 100644 Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml create mode 100644 Api.ApiClients.Entity/.gitignore create mode 100644 Api.ApiClients.Entity/.kubernetes/autoscaler.yaml create mode 100644 Api.ApiClients.Entity/.kubernetes/configmap.yaml create mode 100644 Api.ApiClients.Entity/.kubernetes/deployment.yaml create mode 100644 Api.ApiClients.Entity/.kubernetes/service.yaml create mode 100644 Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Properties/DoNotParallelize.cs create mode 100644 Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Tests.Api.ApiClients.Entity.csproj create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api/NanoApiClient.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Example.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Api.ApiClients.Entity.Service.csproj create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Controllers/ExamplesController.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/Mappings/ExampleMapping.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContext.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContextFactory.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.Designer.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Program.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Properties/InternalsVisibleTo.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Development.json create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Production.json create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Staging.json create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity.sln create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Api.ApiClients.Entity.csproj create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Controllers/ExamplesController.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Program.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/Properties/InternalsVisibleTo.cs create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Development.json create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Production.json create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Staging.json create mode 100644 Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json create mode 100644 Api.ApiClients.Entity/Dockerfile create mode 100644 Api.ApiClients.Entity/LICENSE create mode 100644 Api.ApiClients.Entity/README.md create mode 100644 Api.ApiClients.Entity/icon.png create mode 100644 Api.ApiClients.RootLogIn/.docker/docker-compose.dcproj create mode 100644 Api.ApiClients.RootLogIn/.docker/docker-compose.yml create mode 100644 Api.ApiClients.RootLogIn/.dockerignore create mode 100644 Api.ApiClients.RootLogIn/.github/config/slack.yml create mode 100644 Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml create mode 100644 Api.ApiClients.RootLogIn/.gitignore create mode 100644 Api.ApiClients.RootLogIn/.kubernetes/autoscaler.yaml create mode 100644 Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml create mode 100644 Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml create mode 100644 Api.ApiClients.RootLogIn/.kubernetes/service.yaml create mode 100644 Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Properties/DoNotParallelize.cs create mode 100644 Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Tests.Api.ApiClients.RootLogIn.csproj create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.sln create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/NanoApiClient.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/AutoAuthenticateRootRequest.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/BaseCustomsRequest.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Api.ApiClients.RootLogin.Service.csproj create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/AuthController.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/CustomsController.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Program.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Properties/InternalsVisibleTo.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Development.json create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Production.json create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Staging.json create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Api.ApiClients.RootLogIn.csproj create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Controllers/ExamplesController.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Program.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Properties/InternalsVisibleTo.cs create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Development.json create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Production.json create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Staging.json create mode 100644 Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.json create mode 100644 Api.ApiClients.RootLogIn/Dockerfile create mode 100644 Api.ApiClients.RootLogIn/LICENSE create mode 100644 Api.ApiClients.RootLogIn/README.md create mode 100644 Api.ApiClients.RootLogIn/icon.png create mode 100644 Api.ApiClients/.docker/docker-compose.dcproj create mode 100644 Api.ApiClients/.docker/docker-compose.yml create mode 100644 Api.ApiClients/.dockerignore create mode 100644 Api.ApiClients/.github/config/slack.yml create mode 100644 Api.ApiClients/.github/workflows/build-and-deploy.yml create mode 100644 Api.ApiClients/.gitignore create mode 100644 Api.ApiClients/.kubernetes/autoscaler.yaml create mode 100644 Api.ApiClients/.kubernetes/configmap.yaml create mode 100644 Api.ApiClients/.kubernetes/deployment.yaml create mode 100644 Api.ApiClients/.kubernetes/service.yaml create mode 100644 Api.ApiClients/.tests/Tests.Api.ApiClients/Properties/DoNotParallelize.cs create mode 100644 Api.ApiClients/.tests/Tests.Api.ApiClients/Tests.Api.ApiClients.csproj create mode 100644 Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/NanoApiClient.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BadRequestExceptionRequest.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BaseCustomsRequest.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileBodyRequest.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileRequest.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomRequest.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomBody.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomFileBody.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/ProblemDetailsExceptionRequest.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/RequestTracingRequest.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileBodyResponse.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileResponse.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomResponse.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/RequestTracingResponse.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service/Api.ApiClients.Service.csproj create mode 100644 Api.ApiClients/Api.ApiClients.Service/Controllers/CustomsController.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local create mode 100644 Api.ApiClients/Api.ApiClients.Service/Program.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service/Properties/InternalsVisibleTo.cs create mode 100644 Api.ApiClients/Api.ApiClients.Service/appsettings.Development.json create mode 100644 Api.ApiClients/Api.ApiClients.Service/appsettings.Production.json create mode 100644 Api.ApiClients/Api.ApiClients.Service/appsettings.Staging.json create mode 100644 Api.ApiClients/Api.ApiClients.Service/appsettings.json create mode 100644 Api.ApiClients/Api.ApiClients.sln create mode 100644 Api.ApiClients/Api.ApiClients/Api.ApiClients.csproj create mode 100644 Api.ApiClients/Api.ApiClients/Controllers/ExamplesController.cs create mode 100644 Api.ApiClients/Api.ApiClients/Dockerfile.Local create mode 100644 Api.ApiClients/Api.ApiClients/Program.cs create mode 100644 Api.ApiClients/Api.ApiClients/Properties/InternalsVisibleTo.cs create mode 100644 Api.ApiClients/Api.ApiClients/appsettings.Development.json create mode 100644 Api.ApiClients/Api.ApiClients/appsettings.Production.json create mode 100644 Api.ApiClients/Api.ApiClients/appsettings.Staging.json create mode 100644 Api.ApiClients/Api.ApiClients/appsettings.json create mode 100644 Api.ApiClients/Dockerfile create mode 100644 Api.ApiClients/LICENSE create mode 100644 Api.ApiClients/README.md create mode 100644 Api.ApiClients/icon.png create mode 100644 Api.Auth.External.Custom/.docker/docker-compose.dcproj create mode 100644 Api.Auth.External.Custom/.docker/docker-compose.yml create mode 100644 Api.Auth.External.Custom/.dockerignore create mode 100644 Api.Auth.External.Custom/.github/config/slack.yml create mode 100644 Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml create mode 100644 Api.Auth.External.Custom/.gitignore create mode 100644 Api.Auth.External.Custom/.kubernetes/auth-jwt-secret.yaml create mode 100644 Api.Auth.External.Custom/.kubernetes/autoscaler.yaml create mode 100644 Api.Auth.External.Custom/.kubernetes/configmap.yaml create mode 100644 Api.Auth.External.Custom/.kubernetes/deployment.yaml create mode 100644 Api.Auth.External.Custom/.kubernetes/service.yaml create mode 100644 Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Properties/DoNotParallelize.cs create mode 100644 Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Tests.Api.Auth.External.Custom.csproj create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom.sln create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Api.Auth.External.Custom.csproj create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/AuthController.cs create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/ExamplesController.cs create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Program.cs create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/Properties/InternalsVisibleTo.cs create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Development.json create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Production.json create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Staging.json create mode 100644 Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.json create mode 100644 Api.Auth.External.Custom/Dockerfile create mode 100644 Api.Auth.External.Custom/LICENSE create mode 100644 Api.Auth.External.Custom/README.md create mode 100644 Api.Auth.External.Custom/icon.png create mode 100644 Api.Auth.RootLogin/.docker/docker-compose.dcproj create mode 100644 Api.Auth.RootLogin/.docker/docker-compose.yml create mode 100644 Api.Auth.RootLogin/.dockerignore create mode 100644 Api.Auth.RootLogin/.github/config/slack.yml create mode 100644 Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml create mode 100644 Api.Auth.RootLogin/.gitignore create mode 100644 Api.Auth.RootLogin/.kubernetes/auth-jwt-secret.yaml create mode 100644 Api.Auth.RootLogin/.kubernetes/autoscaler.yaml create mode 100644 Api.Auth.RootLogin/.kubernetes/configmap.yaml create mode 100644 Api.Auth.RootLogin/.kubernetes/deployment.yaml create mode 100644 Api.Auth.RootLogin/.kubernetes/service.yaml create mode 100644 Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Properties/DoNotParallelize.cs create mode 100644 Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Tests.Api.Auth.RootLogin.csproj create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin.sln create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Api.Auth.RootLogin.csproj create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/AuthController.cs create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/ExamplesController.cs create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Program.cs create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/Properties/InternalsVisibleTo.cs create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Development.json create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Production.json create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Staging.json create mode 100644 Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.json create mode 100644 Api.Auth.RootLogin/Dockerfile create mode 100644 Api.Auth.RootLogin/LICENSE create mode 100644 Api.Auth.RootLogin/README.md create mode 100644 Api.Auth.RootLogin/icon.png create mode 100644 Api.Authorization/.docker/docker-compose.dcproj create mode 100644 Api.Authorization/.docker/docker-compose.yml create mode 100644 Api.Authorization/.dockerignore create mode 100644 Api.Authorization/.github/config/slack.yml create mode 100644 Api.Authorization/.github/workflows/build-and-deploy.yml create mode 100644 Api.Authorization/.gitignore create mode 100644 Api.Authorization/.kubernetes/auth-jwt-secret.yaml create mode 100644 Api.Authorization/.kubernetes/autoscaler.yaml create mode 100644 Api.Authorization/.kubernetes/configmap.yaml create mode 100644 Api.Authorization/.kubernetes/deployment.yaml create mode 100644 Api.Authorization/.kubernetes/service.yaml create mode 100644 Api.Authorization/.tests/Tests.Api.Authorization/Properties/DoNotParallelize.cs create mode 100644 Api.Authorization/.tests/Tests.Api.Authorization/Tests.Api.Authorization.csproj create mode 100644 Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj create mode 100644 Api.Authorization/Api.Authorization.sln create mode 100644 Api.Authorization/Api.Authorization/Api.Authorization.csproj create mode 100644 Api.Authorization/Api.Authorization/Controllers/AuthController.cs create mode 100644 Api.Authorization/Api.Authorization/Controllers/ExamplesController.cs create mode 100644 Api.Authorization/Api.Authorization/Dockerfile.Local create mode 100644 Api.Authorization/Api.Authorization/Extensions/ServiceCollectionExtensions.cs create mode 100644 Api.Authorization/Api.Authorization/Program.cs create mode 100644 Api.Authorization/Api.Authorization/Properties/InternalsVisibleTo.cs create mode 100644 Api.Authorization/Api.Authorization/appsettings.Development.json create mode 100644 Api.Authorization/Api.Authorization/appsettings.Production.json create mode 100644 Api.Authorization/Api.Authorization/appsettings.Staging.json create mode 100644 Api.Authorization/Api.Authorization/appsettings.json create mode 100644 Api.Authorization/Dockerfile create mode 100644 Api.Authorization/LICENSE create mode 100644 Api.Authorization/README.md create mode 100644 Api.Authorization/icon.png create mode 100644 Api.ContentNegotiation/.docker/docker-compose.dcproj create mode 100644 Api.ContentNegotiation/.docker/docker-compose.yml create mode 100644 Api.ContentNegotiation/.dockerignore create mode 100644 Api.ContentNegotiation/.github/config/slack.yml create mode 100644 Api.ContentNegotiation/.github/workflows/build-and-deploy.yml create mode 100644 Api.ContentNegotiation/.gitignore create mode 100644 Api.ContentNegotiation/.kubernetes/autoscaler.yaml create mode 100644 Api.ContentNegotiation/.kubernetes/configmap.yaml create mode 100644 Api.ContentNegotiation/.kubernetes/deployment.yaml create mode 100644 Api.ContentNegotiation/.kubernetes/service.yaml create mode 100644 Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Properties/DoNotParallelize.cs create mode 100644 Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Tests.Api.ContentNegotiation.csproj create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation.sln create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Api.ContentNegotiation.csproj create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Controllers/ExamplesController.cs create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Program.cs create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/Properties/InternalsVisibleTo.cs create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Development.json create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Production.json create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Staging.json create mode 100644 Api.ContentNegotiation/Api.ContentNegotiation/appsettings.json create mode 100644 Api.ContentNegotiation/Dockerfile create mode 100644 Api.ContentNegotiation/LICENSE create mode 100644 Api.ContentNegotiation/README.md create mode 100644 Api.ContentNegotiation/icon.png create mode 100644 Api.Cookies/.docker/docker-compose.dcproj create mode 100644 Api.Cookies/.docker/docker-compose.yml create mode 100644 Api.Cookies/.dockerignore create mode 100644 Api.Cookies/.github/config/slack.yml create mode 100644 Api.Cookies/.github/workflows/build-and-deploy.yml create mode 100644 Api.Cookies/.gitignore create mode 100644 Api.Cookies/.kubernetes/autoscaler.yaml create mode 100644 Api.Cookies/.kubernetes/configmap.yaml create mode 100644 Api.Cookies/.kubernetes/deployment.yaml create mode 100644 Api.Cookies/.kubernetes/service.yaml create mode 100644 Api.Cookies/.tests/Tests.Api.Cookies/Properties/DoNotParallelize.cs create mode 100644 Api.Cookies/.tests/Tests.Api.Cookies/Tests.Api.Cookies.csproj create mode 100644 Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj create mode 100644 Api.Cookies/Api.Cookies.sln create mode 100644 Api.Cookies/Api.Cookies/Api.Cookies.csproj create mode 100644 Api.Cookies/Api.Cookies/Controllers/ExamplesController.cs create mode 100644 Api.Cookies/Api.Cookies/Dockerfile.Local create mode 100644 Api.Cookies/Api.Cookies/Program.cs create mode 100644 Api.Cookies/Api.Cookies/Properties/InternalsVisibleTo.cs create mode 100644 Api.Cookies/Api.Cookies/appsettings.Development.json create mode 100644 Api.Cookies/Api.Cookies/appsettings.Production.json create mode 100644 Api.Cookies/Api.Cookies/appsettings.Staging.json create mode 100644 Api.Cookies/Api.Cookies/appsettings.json create mode 100644 Api.Cookies/Dockerfile create mode 100644 Api.Cookies/LICENSE create mode 100644 Api.Cookies/README.md create mode 100644 Api.Cookies/icon.png create mode 100644 Api.CustomConfigSection/.docker/docker-compose.dcproj create mode 100644 Api.CustomConfigSection/.docker/docker-compose.yml create mode 100644 Api.CustomConfigSection/.dockerignore create mode 100644 Api.CustomConfigSection/.github/config/slack.yml create mode 100644 Api.CustomConfigSection/.github/workflows/build-and-deploy.yml create mode 100644 Api.CustomConfigSection/.gitignore create mode 100644 Api.CustomConfigSection/.kubernetes/autoscaler.yaml create mode 100644 Api.CustomConfigSection/.kubernetes/configmap.yaml create mode 100644 Api.CustomConfigSection/.kubernetes/deployment.yaml create mode 100644 Api.CustomConfigSection/.kubernetes/service.yaml create mode 100644 Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Properties/DoNotParallelize.cs create mode 100644 Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Tests.Api.CustomConfigSection.csproj create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection.sln create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Api.CustomConfigSection.csproj create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Config/CustomOptions.cs create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Controllers/ExamplesController.cs create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Program.cs create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/Properties/InternalsVisibleTo.cs create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Development.json create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Production.json create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Staging.json create mode 100644 Api.CustomConfigSection/Api.CustomConfigSection/appsettings.json create mode 100644 Api.CustomConfigSection/Dockerfile create mode 100644 Api.CustomConfigSection/LICENSE create mode 100644 Api.CustomConfigSection/README.md create mode 100644 Api.CustomConfigSection/icon.png create mode 100644 Api.CustomMiddleware/.docker/docker-compose.dcproj create mode 100644 Api.CustomMiddleware/.docker/docker-compose.yml create mode 100644 Api.CustomMiddleware/.dockerignore create mode 100644 Api.CustomMiddleware/.github/config/slack.yml create mode 100644 Api.CustomMiddleware/.github/workflows/build-and-deploy.yml create mode 100644 Api.CustomMiddleware/.gitignore create mode 100644 Api.CustomMiddleware/.kubernetes/autoscaler.yaml create mode 100644 Api.CustomMiddleware/.kubernetes/configmap.yaml create mode 100644 Api.CustomMiddleware/.kubernetes/deployment.yaml create mode 100644 Api.CustomMiddleware/.kubernetes/service.yaml create mode 100644 Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Properties/DoNotParallelize.cs create mode 100644 Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Tests.Api.CustomMiddleware.csproj create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware.sln create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Api.CustomMiddleware.csproj create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Controllers/ExamplesController.cs create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Program.cs create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/Properties/InternalsVisibleTo.cs create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Development.json create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Production.json create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Staging.json create mode 100644 Api.CustomMiddleware/Api.CustomMiddleware/appsettings.json create mode 100644 Api.CustomMiddleware/Dockerfile create mode 100644 Api.CustomMiddleware/LICENSE create mode 100644 Api.CustomMiddleware/README.md create mode 100644 Api.CustomMiddleware/icon.png create mode 100644 Api.CustomService/.docker/docker-compose.dcproj create mode 100644 Api.CustomService/.docker/docker-compose.yml create mode 100644 Api.CustomService/.dockerignore create mode 100644 Api.CustomService/.github/config/slack.yml create mode 100644 Api.CustomService/.github/workflows/build-and-deploy.yml create mode 100644 Api.CustomService/.gitignore create mode 100644 Api.CustomService/.kubernetes/autoscaler.yaml create mode 100644 Api.CustomService/.kubernetes/configmap.yaml create mode 100644 Api.CustomService/.kubernetes/deployment.yaml create mode 100644 Api.CustomService/.kubernetes/service.yaml create mode 100644 Api.CustomService/.tests/Tests.Api.CustomService/Properties/DoNotParallelize.cs create mode 100644 Api.CustomService/.tests/Tests.Api.CustomService/Tests.Api.CustomService.csproj create mode 100644 Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj create mode 100644 Api.CustomService/Api.CustomService.sln create mode 100644 Api.CustomService/Api.CustomService/Api.CustomService.csproj create mode 100644 Api.CustomService/Api.CustomService/Controllers/ExamplesController.cs create mode 100644 Api.CustomService/Api.CustomService/Dockerfile.Local create mode 100644 Api.CustomService/Api.CustomService/Program.cs create mode 100644 Api.CustomService/Api.CustomService/Properties/InternalsVisibleTo.cs create mode 100644 Api.CustomService/Api.CustomService/Services/Abstractions/IExampleService.cs create mode 100644 Api.CustomService/Api.CustomService/Services/ExampleService.cs create mode 100644 Api.CustomService/Api.CustomService/appsettings.Development.json create mode 100644 Api.CustomService/Api.CustomService/appsettings.Production.json create mode 100644 Api.CustomService/Api.CustomService/appsettings.Staging.json create mode 100644 Api.CustomService/Api.CustomService/appsettings.json create mode 100644 Api.CustomService/Dockerfile create mode 100644 Api.CustomService/LICENSE create mode 100644 Api.CustomService/README.md create mode 100644 Api.CustomService/icon.png create mode 100644 Api.Data.Audit/.docker/docker-compose.dcproj create mode 100644 Api.Data.Audit/.docker/docker-compose.yml create mode 100644 Api.Data.Audit/.dockerignore create mode 100644 Api.Data.Audit/.github/config/slack.yml create mode 100644 Api.Data.Audit/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.Audit/.gitignore create mode 100644 Api.Data.Audit/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.Audit/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.Audit/.kubernetes/configmap.yaml create mode 100644 Api.Data.Audit/.kubernetes/deployment.yaml create mode 100644 Api.Data.Audit/.kubernetes/service.yaml create mode 100644 Api.Data.Audit/.tests/Tests.Api.Data.Audit/Properties/DoNotParallelize.cs create mode 100644 Api.Data.Audit/.tests/Tests.Api.Data.Audit/Tests.Api.Data.Audit.csproj create mode 100644 Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj create mode 100644 Api.Data.Audit/Api.Data.Audit.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.Audit/Api.Data.Audit.Models/Example.cs create mode 100644 Api.Data.Audit/Api.Data.Audit.Models/ExampleNavigation.cs create mode 100644 Api.Data.Audit/Api.Data.Audit.Models/ExampleNoAudit.cs create mode 100644 Api.Data.Audit/Api.Data.Audit.sln create mode 100644 Api.Data.Audit/Api.Data.Audit/Api.Data.Audit.csproj create mode 100644 Api.Data.Audit/Api.Data.Audit/Controllers/AuditController.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Controllers/ExampleNoAuditsController.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Controllers/ExamplesController.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNavigationMapping.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNoAuditMapping.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContext.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Dockerfile.Local create mode 100644 Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.Designer.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Program.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.Audit/Api.Data.Audit/appsettings.Development.json create mode 100644 Api.Data.Audit/Api.Data.Audit/appsettings.Production.json create mode 100644 Api.Data.Audit/Api.Data.Audit/appsettings.Staging.json create mode 100644 Api.Data.Audit/Api.Data.Audit/appsettings.json create mode 100644 Api.Data.Audit/Dockerfile create mode 100644 Api.Data.Audit/LICENSE create mode 100644 Api.Data.Audit/README.md create mode 100644 Api.Data.Audit/icon.png create mode 100644 Api.Data.EntityEvents/.docker/docker-compose.dcproj create mode 100644 Api.Data.EntityEvents/.docker/docker-compose.yml create mode 100644 Api.Data.EntityEvents/.docker/init.sql create mode 100644 Api.Data.EntityEvents/.dockerignore create mode 100644 Api.Data.EntityEvents/.github/config/slack.yml create mode 100644 Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.EntityEvents/.gitignore create mode 100644 Api.Data.EntityEvents/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.EntityEvents/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.EntityEvents/.kubernetes/configmap.yaml create mode 100644 Api.Data.EntityEvents/.kubernetes/deployment.yaml create mode 100644 Api.Data.EntityEvents/.kubernetes/service.yaml create mode 100644 Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Properties/DoNotParallelize.cs create mode 100644 Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Tests.Api.Data.EntityEvents.csproj create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Address.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Criterias/DefaultQueryCriteria.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Customer.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Order.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Owned/ProfileSettings.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Person.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Profile.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Customer.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Api.Data.EntityEvents.Subscriber.csproj create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/Mappings/CustomerMapping.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContext.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.Designer.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Program.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Production.json create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Staging.json create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.json create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents.sln create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Api.Data.EntityEvents.csproj create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/AddresssController.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/CustomersController.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/OrdersController.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/ProfilesController.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/AddressMapping.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/CustomerMapping.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/OrderMapping.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/PersonMapping.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/ProfileMapping.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContext.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.Designer.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Program.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Production.json create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Staging.json create mode 100644 Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.json create mode 100644 Api.Data.EntityEvents/Dockerfile create mode 100644 Api.Data.EntityEvents/LICENSE create mode 100644 Api.Data.EntityEvents/README.md create mode 100644 Api.Data.EntityEvents/icon.png create mode 100644 Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.dcproj create mode 100644 Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml create mode 100644 Api.Data.Identity.Auth.ApiKey/.dockerignore create mode 100644 Api.Data.Identity.Auth.ApiKey/.github/config/slack.yml create mode 100644 Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.Identity.Auth.ApiKey/.gitignore create mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-apikey-secret.yaml create mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-jwt-secret.yaml create mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml create mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml create mode 100644 Api.Data.Identity.Auth.ApiKey/.kubernetes/service.yaml create mode 100644 Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Properties/DoNotParallelize.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Tests.Api.Data.Identity.Auth.ApiKey.csproj create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Criterias/UserQueryCriteria.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/User.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.sln create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.csproj create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/AuthController.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/UsersController.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/Mappings/UserMapping.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContext.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.Designer.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Program.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Production.json create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Staging.json create mode 100644 Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.json create mode 100644 Api.Data.Identity.Auth.ApiKey/Dockerfile create mode 100644 Api.Data.Identity.Auth.ApiKey/LICENSE create mode 100644 Api.Data.Identity.Auth.ApiKey/README.md create mode 100644 Api.Data.Identity.Auth.ApiKey/icon.png create mode 100644 Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.dcproj create mode 100644 Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml create mode 100644 Api.Data.Identity.Auth.External.Custom/.dockerignore create mode 100644 Api.Data.Identity.Auth.External.Custom/.github/config/slack.yml create mode 100644 Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.Identity.Auth.External.Custom/.gitignore create mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-jwt-secret.yaml create mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml create mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml create mode 100644 Api.Data.Identity.Auth.External.Custom/.kubernetes/service.yaml create mode 100644 Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Properties/DoNotParallelize.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Tests.Api.Data.Identity.Auth.External.Custom.csproj create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Criterias/UserQueryCriteria.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/User.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.sln create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.csproj create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/AuthController.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/UsersController.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/Mappings/UserMapping.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContext.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.Designer.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Program.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Production.json create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Staging.json create mode 100644 Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.json create mode 100644 Api.Data.Identity.Auth.External.Custom/Dockerfile create mode 100644 Api.Data.Identity.Auth.External.Custom/LICENSE create mode 100644 Api.Data.Identity.Auth.External.Custom/README.md create mode 100644 Api.Data.Identity.Auth.External.Custom/icon.png create mode 100644 Api.Data.Identity.Auth.Jwt/.docker/docker-compose.dcproj create mode 100644 Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml create mode 100644 Api.Data.Identity.Auth.Jwt/.dockerignore create mode 100644 Api.Data.Identity.Auth.Jwt/.github/config/slack.yml create mode 100644 Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.Identity.Auth.Jwt/.gitignore create mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/auth-jwt-secret.yaml create mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml create mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml create mode 100644 Api.Data.Identity.Auth.Jwt/.kubernetes/service.yaml create mode 100644 Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Properties/DoNotParallelize.cs create mode 100644 Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Tests.Api.Data.Identity.Auth.Jwt.csproj create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Criterias/UserQueryCriteria.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/User.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.sln create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.csproj create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/AuthController.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/UsersController.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/Mappings/UserMapping.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContext.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.Designer.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Program.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Production.json create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Staging.json create mode 100644 Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.json create mode 100644 Api.Data.Identity.Auth.Jwt/Dockerfile create mode 100644 Api.Data.Identity.Auth.Jwt/LICENSE create mode 100644 Api.Data.Identity.Auth.Jwt/README.md create mode 100644 Api.Data.Identity.Auth.Jwt/icon.png create mode 100644 Api.Data.Identity/.docker/docker-compose.dcproj create mode 100644 Api.Data.Identity/.docker/docker-compose.yml create mode 100644 Api.Data.Identity/.dockerignore create mode 100644 Api.Data.Identity/.github/config/slack.yml create mode 100644 Api.Data.Identity/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.Identity/.gitignore create mode 100644 Api.Data.Identity/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.Identity/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.Identity/.kubernetes/configmap.yaml create mode 100644 Api.Data.Identity/.kubernetes/deployment.yaml create mode 100644 Api.Data.Identity/.kubernetes/service.yaml create mode 100644 Api.Data.Identity/.tests/Tests.Api.Data.Identity/Properties/DoNotParallelize.cs create mode 100644 Api.Data.Identity/.tests/Tests.Api.Data.Identity/Tests.Api.Data.Identity.csproj create mode 100644 Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj create mode 100644 Api.Data.Identity/Api.Data.Identity.Models/Criterias/UserQueryCriteria.cs create mode 100644 Api.Data.Identity/Api.Data.Identity.Models/User.cs create mode 100644 Api.Data.Identity/Api.Data.Identity.sln create mode 100644 Api.Data.Identity/Api.Data.Identity/Api.Data.Identity.csproj create mode 100644 Api.Data.Identity/Api.Data.Identity/Controllers/UsersController.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/Data/Mappings/UserMapping.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContext.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/Dockerfile.Local create mode 100644 Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.Designer.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/Program.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.Identity/Api.Data.Identity/appsettings.Development.json create mode 100644 Api.Data.Identity/Api.Data.Identity/appsettings.Production.json create mode 100644 Api.Data.Identity/Api.Data.Identity/appsettings.Staging.json create mode 100644 Api.Data.Identity/Api.Data.Identity/appsettings.json create mode 100644 Api.Data.Identity/Dockerfile create mode 100644 Api.Data.Identity/LICENSE create mode 100644 Api.Data.Identity/README.md create mode 100644 Api.Data.Identity/icon.png create mode 100644 Api.Data.InMemory/.docker/docker-compose.dcproj create mode 100644 Api.Data.InMemory/.docker/docker-compose.yml create mode 100644 Api.Data.InMemory/.dockerignore create mode 100644 Api.Data.InMemory/.github/config/slack.yml create mode 100644 Api.Data.InMemory/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.InMemory/.gitignore create mode 100644 Api.Data.InMemory/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.InMemory/.kubernetes/configmap.yaml create mode 100644 Api.Data.InMemory/.kubernetes/deployment.yaml create mode 100644 Api.Data.InMemory/.kubernetes/service.yaml create mode 100644 Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Properties/DoNotParallelize.cs create mode 100644 Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Tests.Api.Data.InMemory.csproj create mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj create mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/Example.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatable.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatableAndEditable.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/ExampleDeletable.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory.Models/ExampleUpdatable.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory.sln create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Api.Data.InMemory.csproj create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreatableAndEditableController.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreateablesController.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleDeletablesController.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleUpdatablesController.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Controllers/ExamplesController.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/InMemoryDbContext.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableMapping.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleDeletableMapping.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleUpdatableMapping.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Program.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.InMemory/Api.Data.InMemory/appsettings.Development.json create mode 100644 Api.Data.InMemory/Api.Data.InMemory/appsettings.Production.json create mode 100644 Api.Data.InMemory/Api.Data.InMemory/appsettings.Staging.json create mode 100644 Api.Data.InMemory/Api.Data.InMemory/appsettings.json create mode 100644 Api.Data.InMemory/Dockerfile create mode 100644 Api.Data.InMemory/LICENSE create mode 100644 Api.Data.InMemory/README.md create mode 100644 Api.Data.InMemory/icon.png create mode 100644 Api.Data.LazyLoading/.docker/docker-compose.dcproj create mode 100644 Api.Data.LazyLoading/.docker/docker-compose.yml create mode 100644 Api.Data.LazyLoading/.dockerignore create mode 100644 Api.Data.LazyLoading/.github/config/slack.yml create mode 100644 Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.LazyLoading/.gitignore create mode 100644 Api.Data.LazyLoading/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.LazyLoading/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.LazyLoading/.kubernetes/configmap.yaml create mode 100644 Api.Data.LazyLoading/.kubernetes/deployment.yaml create mode 100644 Api.Data.LazyLoading/.kubernetes/service.yaml create mode 100644 Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Properties/DoNotParallelize.cs create mode 100644 Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Tests.Api.Data.LazyLoading.csproj create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Example.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelation.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelationIncluded.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading.sln create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Api.Data.LazyLoading.csproj create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Controllers/ExamplesController.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationIncludedMapping.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationMapping.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContext.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.Designer.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Program.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Production.json create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Staging.json create mode 100644 Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.json create mode 100644 Api.Data.LazyLoading/Dockerfile create mode 100644 Api.Data.LazyLoading/LICENSE create mode 100644 Api.Data.LazyLoading/README.md create mode 100644 Api.Data.LazyLoading/icon.png create mode 100644 Api.Data.MySql.Collation/.docker/docker-compose.dcproj create mode 100644 Api.Data.MySql.Collation/.docker/docker-compose.yml create mode 100644 Api.Data.MySql.Collation/.dockerignore create mode 100644 Api.Data.MySql.Collation/.github/config/slack.yml create mode 100644 Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.MySql.Collation/.gitignore create mode 100644 Api.Data.MySql.Collation/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.MySql.Collation/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.MySql.Collation/.kubernetes/configmap.yaml create mode 100644 Api.Data.MySql.Collation/.kubernetes/deployment.yaml create mode 100644 Api.Data.MySql.Collation/.kubernetes/service.yaml create mode 100644 Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Properties/DoNotParallelize.cs create mode 100644 Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Tests.Api.Data.MySql.Collation.csproj create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Example.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation.sln create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Api.Data.MySql.Collation.csproj create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Controllers/ExamplesController.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContext.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.Designer.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Program.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Production.json create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Staging.json create mode 100644 Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json create mode 100644 Api.Data.MySql.Collation/Dockerfile create mode 100644 Api.Data.MySql.Collation/LICENSE create mode 100644 Api.Data.MySql.Collation/README.md create mode 100644 Api.Data.MySql.Collation/icon.png create mode 100644 Api.Data.MySql.Mappings/.docker/docker-compose.dcproj create mode 100644 Api.Data.MySql.Mappings/.docker/docker-compose.yml create mode 100644 Api.Data.MySql.Mappings/.dockerignore create mode 100644 Api.Data.MySql.Mappings/.github/config/slack.yml create mode 100644 Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.MySql.Mappings/.gitignore create mode 100644 Api.Data.MySql.Mappings/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.MySql.Mappings/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.MySql.Mappings/.kubernetes/configmap.yaml create mode 100644 Api.Data.MySql.Mappings/.kubernetes/deployment.yaml create mode 100644 Api.Data.MySql.Mappings/.kubernetes/service.yaml create mode 100644 Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Properties/DoNotParallelize.cs create mode 100644 Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Tests.Api.Data.MySql.Mappings.csproj create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleNormalizedQueryCriteria.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleJson.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleNormalized.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleOwned.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/Profile.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfilePicture.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfileSettings.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.sln create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.csproj create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleJsonController.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleNormalizedController.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleOwnedController.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleJsonMapping.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleNormalizedMapping.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleOwnedMapping.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/EntityTypeBuilderExtensions.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/OwnedNavigationBuilderExtensions.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContext.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.Designer.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Program.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Production.json create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Staging.json create mode 100644 Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.json create mode 100644 Api.Data.MySql.Mappings/Dockerfile create mode 100644 Api.Data.MySql.Mappings/LICENSE create mode 100644 Api.Data.MySql.Mappings/README.md create mode 100644 Api.Data.MySql.Mappings/icon.png create mode 100644 Api.Data.MySql.Spatial/.docker/docker-compose.dcproj create mode 100644 Api.Data.MySql.Spatial/.docker/docker-compose.yml create mode 100644 Api.Data.MySql.Spatial/.dockerignore create mode 100644 Api.Data.MySql.Spatial/.github/config/slack.yml create mode 100644 Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.MySql.Spatial/.gitignore create mode 100644 Api.Data.MySql.Spatial/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.MySql.Spatial/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.MySql.Spatial/.kubernetes/configmap.yaml create mode 100644 Api.Data.MySql.Spatial/.kubernetes/deployment.yaml create mode 100644 Api.Data.MySql.Spatial/.kubernetes/service.yaml create mode 100644 Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Properties/DoNotParallelize.cs create mode 100644 Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Tests.Api.Data.MySql.Spatial.csproj create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Example.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.sln create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.csproj create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Controllers/ExamplesController.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContext.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.Designer.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Program.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Production.json create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Staging.json create mode 100644 Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.json create mode 100644 Api.Data.MySql.Spatial/Dockerfile create mode 100644 Api.Data.MySql.Spatial/LICENSE create mode 100644 Api.Data.MySql.Spatial/README.md create mode 100644 Api.Data.MySql.Spatial/icon.png create mode 100644 Api.Data.MySql.StoredProcedures/.docker/docker-compose.dcproj create mode 100644 Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml create mode 100644 Api.Data.MySql.StoredProcedures/.dockerignore create mode 100644 Api.Data.MySql.StoredProcedures/.github/config/slack.yml create mode 100644 Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.MySql.StoredProcedures/.gitignore create mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml create mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml create mode 100644 Api.Data.MySql.StoredProcedures/.kubernetes/service.yaml create mode 100644 Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Properties/DoNotParallelize.cs create mode 100644 Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Tests.Api.Data.MySql.StoredProcedures.csproj create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Example.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/ExampleResult.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.sln create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.csproj create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Controllers/ExamplesController.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Extensions/RepositoryExtensions.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContext.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/StoredProcedures/StoredProcedures.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.Designer.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.Designer.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Program.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Production.json create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Staging.json create mode 100644 Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.json create mode 100644 Api.Data.MySql.StoredProcedures/Dockerfile create mode 100644 Api.Data.MySql.StoredProcedures/LICENSE create mode 100644 Api.Data.MySql.StoredProcedures/README.md create mode 100644 Api.Data.MySql.StoredProcedures/icon.png create mode 100644 Api.Data.MySql.Views/.docker/docker-compose.dcproj create mode 100644 Api.Data.MySql.Views/.docker/docker-compose.yml create mode 100644 Api.Data.MySql.Views/.dockerignore create mode 100644 Api.Data.MySql.Views/.github/config/slack.yml create mode 100644 Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.MySql.Views/.gitignore create mode 100644 Api.Data.MySql.Views/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.MySql.Views/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.MySql.Views/.kubernetes/configmap.yaml create mode 100644 Api.Data.MySql.Views/.kubernetes/deployment.yaml create mode 100644 Api.Data.MySql.Views/.kubernetes/service.yaml create mode 100644 Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Properties/DoNotParallelize.cs create mode 100644 Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Tests.Api.Data.MySql.Views.csproj create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Example.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.Models/ExampleView.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views.sln create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Api.Data.MySql.Views.csproj create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExampleViewsController.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExamplesController.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleViewMapping.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContext.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Views/ExampleViewDefinition.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.Designer.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.Designer.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Program.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Production.json create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Staging.json create mode 100644 Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.json create mode 100644 Api.Data.MySql.Views/Dockerfile create mode 100644 Api.Data.MySql.Views/LICENSE create mode 100644 Api.Data.MySql.Views/README.md create mode 100644 Api.Data.MySql.Views/icon.png create mode 100644 Api.Data.MySql/.docker/docker-compose.dcproj create mode 100644 Api.Data.MySql/.docker/docker-compose.yml create mode 100644 Api.Data.MySql/.dockerignore create mode 100644 Api.Data.MySql/.github/config/slack.yml create mode 100644 Api.Data.MySql/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.MySql/.gitignore create mode 100644 Api.Data.MySql/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.MySql/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.MySql/.kubernetes/configmap.yaml create mode 100644 Api.Data.MySql/.kubernetes/deployment.yaml create mode 100644 Api.Data.MySql/.kubernetes/service.yaml create mode 100644 Api.Data.MySql/.tests/Tests.Api.Data.MySql/Properties/DoNotParallelize.cs create mode 100644 Api.Data.MySql/.tests/Tests.Api.Data.MySql/Tests.Api.Data.MySql.csproj create mode 100644 Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj create mode 100644 Api.Data.MySql/Api.Data.MySql.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.MySql/Api.Data.MySql.Models/Example.cs create mode 100644 Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatable.cs create mode 100644 Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatableAndEditable.cs create mode 100644 Api.Data.MySql/Api.Data.MySql.Models/ExampleDeletable.cs create mode 100644 Api.Data.MySql/Api.Data.MySql.Models/ExampleUpdatable.cs create mode 100644 Api.Data.MySql/Api.Data.MySql.sln create mode 100644 Api.Data.MySql/Api.Data.MySql/Api.Data.MySql.csproj create mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreatableAndEditableController.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreateablesController.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExampleDeletablesController.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExampleUpdatablesController.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Controllers/ExamplesController.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableMapping.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleDeletableMapping.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleUpdatableMapping.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContext.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Dockerfile.Local create mode 100644 Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.Designer.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Program.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.MySql/Api.Data.MySql/appsettings.Development.json create mode 100644 Api.Data.MySql/Api.Data.MySql/appsettings.Production.json create mode 100644 Api.Data.MySql/Api.Data.MySql/appsettings.Staging.json create mode 100644 Api.Data.MySql/Api.Data.MySql/appsettings.json create mode 100644 Api.Data.MySql/Dockerfile create mode 100644 Api.Data.MySql/LICENSE create mode 100644 Api.Data.MySql/README.md create mode 100644 Api.Data.MySql/icon.png create mode 100644 Api.Data.PostgreSQL.Spatial/.docker/docker-compose.dcproj create mode 100644 Api.Data.PostgreSQL.Spatial/.docker/docker-compose.yml create mode 100644 Api.Data.PostgreSQL.Spatial/.dockerignore create mode 100644 Api.Data.PostgreSQL.Spatial/.github/config/slack.yml create mode 100644 Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.PostgreSQL.Spatial/.gitignore create mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml create mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml create mode 100644 Api.Data.PostgreSQL.Spatial/.kubernetes/service.yaml create mode 100644 Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Properties/DoNotParallelize.cs create mode 100644 Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Tests.Api.Data.PostgreSQL.Spatial.csproj create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Example.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.sln create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.csproj create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Controllers/ExamplesController.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContext.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContextFactory.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.Designer.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/PostgreSqlDbContextModelSnapshot.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Development.json create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Production.json create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Staging.json create mode 100644 Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.json create mode 100644 Api.Data.PostgreSQL.Spatial/Dockerfile create mode 100644 Api.Data.PostgreSQL.Spatial/LICENSE create mode 100644 Api.Data.PostgreSQL.Spatial/README.md create mode 100644 Api.Data.PostgreSQL.Spatial/icon.png create mode 100644 Api.Data.PostgreSQL/.docker/docker-compose.dcproj create mode 100644 Api.Data.PostgreSQL/.docker/docker-compose.yml create mode 100644 Api.Data.PostgreSQL/.dockerignore create mode 100644 Api.Data.PostgreSQL/.github/config/slack.yml create mode 100644 Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.PostgreSQL/.gitignore create mode 100644 Api.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.PostgreSQL/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.PostgreSQL/.kubernetes/configmap.yaml create mode 100644 Api.Data.PostgreSQL/.kubernetes/deployment.yaml create mode 100644 Api.Data.PostgreSQL/.kubernetes/service.yaml create mode 100644 Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Properties/DoNotParallelize.cs create mode 100644 Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Tests.Api.Data.PostgreSQL.csproj create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Example.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatable.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatableAndEditable.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleDeletable.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleUpdatable.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Api.Data.PostgreSQL.csproj create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreatableAndEditableController.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreateablesController.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleDeletablesController.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleUpdatablesController.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExamplesController.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableMapping.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleDeletableMapping.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleUpdatableMapping.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContext.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.Designer.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Development.json create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Production.json create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Staging.json create mode 100644 Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.json create mode 100644 Api.Data.PostgreSQL/Dockerfile create mode 100644 Api.Data.PostgreSQL/LICENSE create mode 100644 Api.Data.PostgreSQL/README.md create mode 100644 Api.Data.PostgreSQL/icon.png create mode 100644 Api.Data.Repository.AutoSave/.docker/docker-compose.dcproj create mode 100644 Api.Data.Repository.AutoSave/.docker/docker-compose.yml create mode 100644 Api.Data.Repository.AutoSave/.dockerignore create mode 100644 Api.Data.Repository.AutoSave/.github/config/slack.yml create mode 100644 Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.Repository.AutoSave/.gitignore create mode 100644 Api.Data.Repository.AutoSave/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.Repository.AutoSave/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml create mode 100644 Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml create mode 100644 Api.Data.Repository.AutoSave/.kubernetes/service.yaml create mode 100644 Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Properties/DoNotParallelize.cs create mode 100644 Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Tests.Api.Data.Repository.AutoSave.csproj create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Example.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.sln create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.csproj create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Controllers/ExamplesController.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContext.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.Designer.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Program.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Production.json create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Staging.json create mode 100644 Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.json create mode 100644 Api.Data.Repository.AutoSave/Dockerfile create mode 100644 Api.Data.Repository.AutoSave/LICENSE create mode 100644 Api.Data.Repository.AutoSave/README.md create mode 100644 Api.Data.Repository.AutoSave/icon.png create mode 100644 Api.Data.Repository.Includes/.docker/docker-compose.dcproj create mode 100644 Api.Data.Repository.Includes/.docker/docker-compose.yml create mode 100644 Api.Data.Repository.Includes/.dockerignore create mode 100644 Api.Data.Repository.Includes/.github/config/slack.yml create mode 100644 Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.Repository.Includes/.gitignore create mode 100644 Api.Data.Repository.Includes/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.Repository.Includes/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.Repository.Includes/.kubernetes/configmap.yaml create mode 100644 Api.Data.Repository.Includes/.kubernetes/deployment.yaml create mode 100644 Api.Data.Repository.Includes/.kubernetes/service.yaml create mode 100644 Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Properties/DoNotParallelize.cs create mode 100644 Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Tests.Api.Data.Repository.Includes.csproj create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Customer.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/CustomerProfile.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Order.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Payment.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes.sln create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Api.Data.Repository.Includes.csproj create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/ExamplesController.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerProfileResponse.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerResponse.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/OrderResponse.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/PaymentResponse.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerMapping.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerProfileMapping.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/OrderMapping.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/PaymentMapping.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContext.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.Designer.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Program.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Production.json create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Staging.json create mode 100644 Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.json create mode 100644 Api.Data.Repository.Includes/Dockerfile create mode 100644 Api.Data.Repository.Includes/LICENSE create mode 100644 Api.Data.Repository.Includes/README.md create mode 100644 Api.Data.Repository.Includes/icon.png create mode 100644 Api.Data.SoftDelete/.docker/docker-compose.dcproj create mode 100644 Api.Data.SoftDelete/.docker/docker-compose.yml create mode 100644 Api.Data.SoftDelete/.dockerignore create mode 100644 Api.Data.SoftDelete/.github/config/slack.yml create mode 100644 Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.SoftDelete/.gitignore create mode 100644 Api.Data.SoftDelete/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.SoftDelete/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.SoftDelete/.kubernetes/configmap.yaml create mode 100644 Api.Data.SoftDelete/.kubernetes/deployment.yaml create mode 100644 Api.Data.SoftDelete/.kubernetes/service.yaml create mode 100644 Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Properties/DoNotParallelize.cs create mode 100644 Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Tests.Api.Data.SoftDelete.csproj create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Example.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete.sln create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Api.Data.SoftDelete.csproj create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Controllers/ExamplesController.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContext.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.Designer.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Program.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Production.json create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Staging.json create mode 100644 Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.json create mode 100644 Api.Data.SoftDelete/Dockerfile create mode 100644 Api.Data.SoftDelete/LICENSE create mode 100644 Api.Data.SoftDelete/README.md create mode 100644 Api.Data.SoftDelete/icon.png create mode 100644 Api.Data.SqLite/.docker/docker-compose.dcproj create mode 100644 Api.Data.SqLite/.docker/docker-compose.yml create mode 100644 Api.Data.SqLite/.dockerignore create mode 100644 Api.Data.SqLite/.github/config/slack.yml create mode 100644 Api.Data.SqLite/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.SqLite/.gitignore create mode 100644 Api.Data.SqLite/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.SqLite/.kubernetes/configmap.yaml create mode 100644 Api.Data.SqLite/.kubernetes/data-pvc.yaml create mode 100644 Api.Data.SqLite/.kubernetes/data-storageclass.yaml create mode 100644 Api.Data.SqLite/.kubernetes/deployment.yaml create mode 100644 Api.Data.SqLite/.kubernetes/service.yaml create mode 100644 Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Properties/DoNotParallelize.cs create mode 100644 Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Tests.Api.Data.SqLite.csproj create mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj create mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/Example.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatable.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatableAndEditable.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/ExampleDeletable.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite.Models/ExampleUpdatable.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite.sln create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Api.Data.SqLite.csproj create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreatableAndEditableController.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreateablesController.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleDeletablesController.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleUpdatablesController.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Controllers/ExamplesController.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableMapping.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleDeletableMapping.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleUpdatableMapping.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContext.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContextFactory.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Migrations/20260427141022_Initial.Designer.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Migrations/20260427141022_Initial.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Program.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json create mode 100644 Api.Data.SqLite/Api.Data.SqLite/appsettings.Production.json create mode 100644 Api.Data.SqLite/Api.Data.SqLite/appsettings.Staging.json create mode 100644 Api.Data.SqLite/Api.Data.SqLite/appsettings.json create mode 100644 Api.Data.SqLite/Dockerfile create mode 100644 Api.Data.SqLite/LICENSE create mode 100644 Api.Data.SqLite/README.md create mode 100644 Api.Data.SqLite/icon.png create mode 100644 Api.Data.SqlServer.Spatial/.docker/docker-compose.dcproj create mode 100644 Api.Data.SqlServer.Spatial/.docker/docker-compose.yml create mode 100644 Api.Data.SqlServer.Spatial/.dockerignore create mode 100644 Api.Data.SqlServer.Spatial/.github/config/slack.yml create mode 100644 Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.SqlServer.Spatial/.gitignore create mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml create mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml create mode 100644 Api.Data.SqlServer.Spatial/.kubernetes/service.yaml create mode 100644 Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Properties/DoNotParallelize.cs create mode 100644 Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Tests.Api.Data.SqlServer.Spatial.csproj create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Example.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.sln create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.csproj create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Controllers/ExamplesController.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContext.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContextFactory.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.Designer.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.Designer.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/SqlServerDbContextModelSnapshot.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Program.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Development.json create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Production.json create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Staging.json create mode 100644 Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.json create mode 100644 Api.Data.SqlServer.Spatial/Dockerfile create mode 100644 Api.Data.SqlServer.Spatial/LICENSE create mode 100644 Api.Data.SqlServer.Spatial/README.md create mode 100644 Api.Data.SqlServer.Spatial/icon.png create mode 100644 Api.Data.SqlServer/.docker/docker-compose.dcproj create mode 100644 Api.Data.SqlServer/.docker/docker-compose.yml create mode 100644 Api.Data.SqlServer/.dockerignore create mode 100644 Api.Data.SqlServer/.github/config/slack.yml create mode 100644 Api.Data.SqlServer/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.SqlServer/.gitignore create mode 100644 Api.Data.SqlServer/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.SqlServer/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.SqlServer/.kubernetes/configmap.yaml create mode 100644 Api.Data.SqlServer/.kubernetes/deployment.yaml create mode 100644 Api.Data.SqlServer/.kubernetes/service.yaml create mode 100644 Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Properties/DoNotParallelize.cs create mode 100644 Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Tests.Api.Data.SqlServer.csproj create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/Example.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatable.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatableAndEditable.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleDeletable.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleUpdatable.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer.sln create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Api.Data.SqlServer.csproj create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreatableAndEditableController.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreateablesController.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleDeletablesController.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleUpdatablesController.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExamplesController.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableMapping.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleDeletableMapping.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleUpdatableMapping.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContext.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContextFactory.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.Designer.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Program.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Development.json create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Production.json create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Staging.json create mode 100644 Api.Data.SqlServer/Api.Data.SqlServer/appsettings.json create mode 100644 Api.Data.SqlServer/Dockerfile create mode 100644 Api.Data.SqlServer/LICENSE create mode 100644 Api.Data.SqlServer/README.md create mode 100644 Api.Data.SqlServer/icon.png create mode 100644 Api.Data.Triggers/.docker/docker-compose.dcproj create mode 100644 Api.Data.Triggers/.docker/docker-compose.yml create mode 100644 Api.Data.Triggers/.dockerignore create mode 100644 Api.Data.Triggers/.github/config/slack.yml create mode 100644 Api.Data.Triggers/.github/workflows/build-and-deploy.yml create mode 100644 Api.Data.Triggers/.gitignore create mode 100644 Api.Data.Triggers/.kubernetes/auth-sql-secret.yaml create mode 100644 Api.Data.Triggers/.kubernetes/autoscaler.yaml create mode 100644 Api.Data.Triggers/.kubernetes/configmap.yaml create mode 100644 Api.Data.Triggers/.kubernetes/deployment.yaml create mode 100644 Api.Data.Triggers/.kubernetes/service.yaml create mode 100644 Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Properties/DoNotParallelize.cs create mode 100644 Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Tests.Api.Data.Triggers.csproj create mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj create mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleQueryCriteria.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleTriggerQueryCriteria.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/Example.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers.Models/ExampleTrigger.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers.sln create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Api.Data.Triggers.csproj create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Controllers/ExampleTriggersController.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Controllers/ExamplesController.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleMapping.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleTriggerMapping.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContext.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContextFactory.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Data/Triggers/ExampleTriggers.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.Designer.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Program.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/Properties/InternalsVisibleTo.cs create mode 100644 Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json create mode 100644 Api.Data.Triggers/Api.Data.Triggers/appsettings.Production.json create mode 100644 Api.Data.Triggers/Api.Data.Triggers/appsettings.Staging.json create mode 100644 Api.Data.Triggers/Api.Data.Triggers/appsettings.json create mode 100644 Api.Data.Triggers/Dockerfile create mode 100644 Api.Data.Triggers/LICENSE create mode 100644 Api.Data.Triggers/README.md create mode 100644 Api.Data.Triggers/icon.png create mode 100644 Api.Documentation.Csp/.docker/docker-compose.dcproj create mode 100644 Api.Documentation.Csp/.docker/docker-compose.yml create mode 100644 Api.Documentation.Csp/.dockerignore create mode 100644 Api.Documentation.Csp/.github/config/slack.yml create mode 100644 Api.Documentation.Csp/.github/workflows/build-and-deploy.yml create mode 100644 Api.Documentation.Csp/.gitignore create mode 100644 Api.Documentation.Csp/.kubernetes/autoscaler.yaml create mode 100644 Api.Documentation.Csp/.kubernetes/configmap.yaml create mode 100644 Api.Documentation.Csp/.kubernetes/deployment.yaml create mode 100644 Api.Documentation.Csp/.kubernetes/service.yaml create mode 100644 Api.Documentation.Csp/.tests/Tests.Api.Documentation.Csp/Properties/DoNotParallelize.cs create mode 100644 Api.Documentation.Csp/.tests/Tests.Api.Documentation.Csp/Tests.Api.Documentation.Csp.csproj create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp.Models/Api.Documentation.Csp.Models.csproj create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp.sln create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/Api.Documentation.Csp.csproj create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/Controllers/ExamplesController.cs create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/Dockerfile.Local create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/Program.cs create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/Properties/InternalsVisibleTo.cs create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Development.json create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Production.json create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Staging.json create mode 100644 Api.Documentation.Csp/Api.Documentation.Csp/appsettings.json create mode 100644 Api.Documentation.Csp/Dockerfile create mode 100644 Api.Documentation.Csp/LICENSE create mode 100644 Api.Documentation.Csp/README.md create mode 100644 Api.Documentation.Csp/icon.png create mode 100644 Api.Documentation/.docker/docker-compose.dcproj create mode 100644 Api.Documentation/.docker/docker-compose.yml create mode 100644 Api.Documentation/.dockerignore create mode 100644 Api.Documentation/.github/config/slack.yml create mode 100644 Api.Documentation/.github/workflows/build-and-deploy.yml create mode 100644 Api.Documentation/.gitignore create mode 100644 Api.Documentation/.kubernetes/autoscaler.yaml create mode 100644 Api.Documentation/.kubernetes/configmap.yaml create mode 100644 Api.Documentation/.kubernetes/deployment.yaml create mode 100644 Api.Documentation/.kubernetes/service.yaml create mode 100644 Api.Documentation/.tests/Tests.Api.Documentation/Properties/DoNotParallelize.cs create mode 100644 Api.Documentation/.tests/Tests.Api.Documentation/Tests.Api.Documentation.csproj create mode 100644 Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj create mode 100644 Api.Documentation/Api.Documentation.sln create mode 100644 Api.Documentation/Api.Documentation/Api.Documentation.csproj create mode 100644 Api.Documentation/Api.Documentation/Controllers/ExamplesController.cs create mode 100644 Api.Documentation/Api.Documentation/Dockerfile.Local create mode 100644 Api.Documentation/Api.Documentation/Program.cs create mode 100644 Api.Documentation/Api.Documentation/Properties/InternalsVisibleTo.cs create mode 100644 Api.Documentation/Api.Documentation/appsettings.Development.json create mode 100644 Api.Documentation/Api.Documentation/appsettings.Production.json create mode 100644 Api.Documentation/Api.Documentation/appsettings.Staging.json create mode 100644 Api.Documentation/Api.Documentation/appsettings.json create mode 100644 Api.Documentation/Dockerfile create mode 100644 Api.Documentation/LICENSE create mode 100644 Api.Documentation/README.md create mode 100644 Api.Documentation/icon.png create mode 100644 Api.ErrorHandling/.docker/docker-compose.dcproj create mode 100644 Api.ErrorHandling/.docker/docker-compose.yml create mode 100644 Api.ErrorHandling/.dockerignore create mode 100644 Api.ErrorHandling/.github/config/slack.yml create mode 100644 Api.ErrorHandling/.github/workflows/build-and-deploy.yml create mode 100644 Api.ErrorHandling/.gitignore create mode 100644 Api.ErrorHandling/.kubernetes/autoscaler.yaml create mode 100644 Api.ErrorHandling/.kubernetes/configmap.yaml create mode 100644 Api.ErrorHandling/.kubernetes/deployment.yaml create mode 100644 Api.ErrorHandling/.kubernetes/service.yaml create mode 100644 Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Properties/DoNotParallelize.cs create mode 100644 Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Tests.Api.ErrorHandling.csproj create mode 100644 Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj create mode 100644 Api.ErrorHandling/Api.ErrorHandling.sln create mode 100644 Api.ErrorHandling/Api.ErrorHandling/Api.ErrorHandling.csproj create mode 100644 Api.ErrorHandling/Api.ErrorHandling/Controllers/ExamplesController.cs create mode 100644 Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local create mode 100644 Api.ErrorHandling/Api.ErrorHandling/Program.cs create mode 100644 Api.ErrorHandling/Api.ErrorHandling/Properties/InternalsVisibleTo.cs create mode 100644 Api.ErrorHandling/Api.ErrorHandling/appsettings.Development.json create mode 100644 Api.ErrorHandling/Api.ErrorHandling/appsettings.Production.json create mode 100644 Api.ErrorHandling/Api.ErrorHandling/appsettings.Staging.json create mode 100644 Api.ErrorHandling/Api.ErrorHandling/appsettings.json create mode 100644 Api.ErrorHandling/Dockerfile create mode 100644 Api.ErrorHandling/LICENSE create mode 100644 Api.ErrorHandling/README.md create mode 100644 Api.ErrorHandling/icon.png create mode 100644 Api.Eventing.RabbitMq/.docker/docker-compose.dcproj create mode 100644 Api.Eventing.RabbitMq/.docker/docker-compose.yml create mode 100644 Api.Eventing.RabbitMq/.dockerignore create mode 100644 Api.Eventing.RabbitMq/.github/config/slack.yml create mode 100644 Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml create mode 100644 Api.Eventing.RabbitMq/.gitignore create mode 100644 Api.Eventing.RabbitMq/.kubernetes/autoscaler.yaml create mode 100644 Api.Eventing.RabbitMq/.kubernetes/configmap.yaml create mode 100644 Api.Eventing.RabbitMq/.kubernetes/deployment.yaml create mode 100644 Api.Eventing.RabbitMq/.kubernetes/service.yaml create mode 100644 Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Properties/DoNotParallelize.cs create mode 100644 Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Tests.Api.Eventing.RabbitMq.csproj create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.sln create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.csproj create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Controllers/ExamplesController.cs create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandler.cs create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandlerRoutingKey.cs create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModel.cs create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModelRoutingKey.cs create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Program.cs create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Development.json create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Production.json create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Staging.json create mode 100644 Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.json create mode 100644 Api.Eventing.RabbitMq/Dockerfile create mode 100644 Api.Eventing.RabbitMq/LICENSE create mode 100644 Api.Eventing.RabbitMq/README.md create mode 100644 Api.Eventing.RabbitMq/icon.png create mode 100644 Api.HealthChecks/.docker/docker-compose.dcproj create mode 100644 Api.HealthChecks/.docker/docker-compose.yml create mode 100644 Api.HealthChecks/.dockerignore create mode 100644 Api.HealthChecks/.github/config/slack.yml create mode 100644 Api.HealthChecks/.github/workflows/build-and-deploy.yml create mode 100644 Api.HealthChecks/.gitignore create mode 100644 Api.HealthChecks/.kubernetes/autoscaler.yaml create mode 100644 Api.HealthChecks/.kubernetes/configmap.yaml create mode 100644 Api.HealthChecks/.kubernetes/deployment.yaml create mode 100644 Api.HealthChecks/.kubernetes/service.yaml create mode 100644 Api.HealthChecks/.tests/Tests.Api.HealthChecks/Properties/DoNotParallelize.cs create mode 100644 Api.HealthChecks/.tests/Tests.Api.HealthChecks/Tests.Api.HealthChecks.csproj create mode 100644 Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj create mode 100644 Api.HealthChecks/Api.HealthChecks.sln create mode 100644 Api.HealthChecks/Api.HealthChecks/Api.HealthChecks.csproj create mode 100644 Api.HealthChecks/Api.HealthChecks/Controllers/ExamplesController.cs create mode 100644 Api.HealthChecks/Api.HealthChecks/Dockerfile.Local create mode 100644 Api.HealthChecks/Api.HealthChecks/Program.cs create mode 100644 Api.HealthChecks/Api.HealthChecks/Properties/InternalsVisibleTo.cs create mode 100644 Api.HealthChecks/Api.HealthChecks/appsettings.Development.json create mode 100644 Api.HealthChecks/Api.HealthChecks/appsettings.Production.json create mode 100644 Api.HealthChecks/Api.HealthChecks/appsettings.Staging.json create mode 100644 Api.HealthChecks/Api.HealthChecks/appsettings.json create mode 100644 Api.HealthChecks/Dockerfile create mode 100644 Api.HealthChecks/LICENSE create mode 100644 Api.HealthChecks/README.md create mode 100644 Api.HealthChecks/icon.png create mode 100644 Api.Hosting.Http/.docker/docker-compose.dcproj create mode 100644 Api.Hosting.Http/.docker/docker-compose.yml create mode 100644 Api.Hosting.Http/.dockerignore create mode 100644 Api.Hosting.Http/.github/config/slack.yml create mode 100644 Api.Hosting.Http/.github/workflows/build-and-deploy.yml create mode 100644 Api.Hosting.Http/.gitignore create mode 100644 Api.Hosting.Http/.kubernetes/autoscaler.yaml create mode 100644 Api.Hosting.Http/.kubernetes/configmap.yaml create mode 100644 Api.Hosting.Http/.kubernetes/deployment.yaml create mode 100644 Api.Hosting.Http/.kubernetes/service.yaml create mode 100644 Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Properties/DoNotParallelize.cs create mode 100644 Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Tests.Api.Hosting.Http.csproj create mode 100644 Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj create mode 100644 Api.Hosting.Http/Api.Hosting.Http.sln create mode 100644 Api.Hosting.Http/Api.Hosting.Http/Api.Hosting.Http.csproj create mode 100644 Api.Hosting.Http/Api.Hosting.Http/Controllers/ExamplesController.cs create mode 100644 Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local create mode 100644 Api.Hosting.Http/Api.Hosting.Http/Program.cs create mode 100644 Api.Hosting.Http/Api.Hosting.Http/Properties/InternalsVisibleTo.cs create mode 100644 Api.Hosting.Http/Api.Hosting.Http/appsettings.Development.json create mode 100644 Api.Hosting.Http/Api.Hosting.Http/appsettings.Production.json create mode 100644 Api.Hosting.Http/Api.Hosting.Http/appsettings.Staging.json create mode 100644 Api.Hosting.Http/Api.Hosting.Http/appsettings.json create mode 100644 Api.Hosting.Http/Dockerfile create mode 100644 Api.Hosting.Http/LICENSE create mode 100644 Api.Hosting.Http/README.md create mode 100644 Api.Hosting.Http/icon.png create mode 100644 Api.Hosting.Https/.docker/docker-compose.dcproj create mode 100644 Api.Hosting.Https/.docker/docker-compose.yml create mode 100644 Api.Hosting.Https/.dockerignore create mode 100644 Api.Hosting.Https/.github/config/slack.yml create mode 100644 Api.Hosting.Https/.github/workflows/build-and-deploy.yml create mode 100644 Api.Hosting.Https/.gitignore create mode 100644 Api.Hosting.Https/.kubernetes/autoscaler.yaml create mode 100644 Api.Hosting.Https/.kubernetes/configmap.yaml create mode 100644 Api.Hosting.Https/.kubernetes/deployment.yaml create mode 100644 Api.Hosting.Https/.kubernetes/httproute-443.yaml create mode 100644 Api.Hosting.Https/.kubernetes/httproute-80.yaml create mode 100644 Api.Hosting.Https/.kubernetes/service.yaml create mode 100644 Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Properties/DoNotParallelize.cs create mode 100644 Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Tests.Api.Hosting.Https.csproj create mode 100644 Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj create mode 100644 Api.Hosting.Https/Api.Hosting.Https.sln create mode 100644 Api.Hosting.Https/Api.Hosting.Https/Api.Hosting.Https.csproj create mode 100644 Api.Hosting.Https/Api.Hosting.Https/Controllers/ExamplesController.cs create mode 100644 Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local create mode 100644 Api.Hosting.Https/Api.Hosting.Https/Program.cs create mode 100644 Api.Hosting.Https/Api.Hosting.Https/Properties/InternalsVisibleTo.cs create mode 100644 Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json create mode 100644 Api.Hosting.Https/Api.Hosting.Https/appsettings.Production.json create mode 100644 Api.Hosting.Https/Api.Hosting.Https/appsettings.Staging.json create mode 100644 Api.Hosting.Https/Api.Hosting.Https/appsettings.json create mode 100644 Api.Hosting.Https/Dockerfile create mode 100644 Api.Hosting.Https/LICENSE create mode 100644 Api.Hosting.Https/README.md create mode 100644 Api.Hosting.Https/icon.png create mode 100644 Api.Hosting.Https/localhost.pfx create mode 100644 Api.Hosting.MultipartLimits/.docker/docker-compose.dcproj create mode 100644 Api.Hosting.MultipartLimits/.docker/docker-compose.yml create mode 100644 Api.Hosting.MultipartLimits/.dockerignore create mode 100644 Api.Hosting.MultipartLimits/.github/config/slack.yml create mode 100644 Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml create mode 100644 Api.Hosting.MultipartLimits/.gitignore create mode 100644 Api.Hosting.MultipartLimits/.kubernetes/autoscaler.yaml create mode 100644 Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml create mode 100644 Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml create mode 100644 Api.Hosting.MultipartLimits/.kubernetes/service.yaml create mode 100644 Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Properties/DoNotParallelize.cs create mode 100644 Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Tests.Api.Hosting.MultipartLimits.csproj create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.sln create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.csproj create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Controllers/ExamplesController.cs create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Program.cs create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Properties/InternalsVisibleTo.cs create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Development.json create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Production.json create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Staging.json create mode 100644 Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.json create mode 100644 Api.Hosting.MultipartLimits/Dockerfile create mode 100644 Api.Hosting.MultipartLimits/LICENSE create mode 100644 Api.Hosting.MultipartLimits/README.md create mode 100644 Api.Hosting.MultipartLimits/icon.png create mode 100644 Api.Localization/.docker/docker-compose.dcproj create mode 100644 Api.Localization/.docker/docker-compose.yml create mode 100644 Api.Localization/.dockerignore create mode 100644 Api.Localization/.github/config/slack.yml create mode 100644 Api.Localization/.github/workflows/build-and-deploy.yml create mode 100644 Api.Localization/.gitignore create mode 100644 Api.Localization/.kubernetes/autoscaler.yaml create mode 100644 Api.Localization/.kubernetes/configmap.yaml create mode 100644 Api.Localization/.kubernetes/deployment.yaml create mode 100644 Api.Localization/.kubernetes/service.yaml create mode 100644 Api.Localization/.tests/Tests.Api.Localization/Properties/DoNotParallelize.cs create mode 100644 Api.Localization/.tests/Tests.Api.Localization/Tests.Api.Localization.csproj create mode 100644 Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj create mode 100644 Api.Localization/Api.Localization.sln create mode 100644 Api.Localization/Api.Localization/Api.Localization.csproj create mode 100644 Api.Localization/Api.Localization/Controllers/ExamplesController.cs create mode 100644 Api.Localization/Api.Localization/Dockerfile.Local create mode 100644 Api.Localization/Api.Localization/Program.cs create mode 100644 Api.Localization/Api.Localization/Properties/InternalsVisibleTo.cs create mode 100644 Api.Localization/Api.Localization/appsettings.Development.json create mode 100644 Api.Localization/Api.Localization/appsettings.Production.json create mode 100644 Api.Localization/Api.Localization/appsettings.Staging.json create mode 100644 Api.Localization/Api.Localization/appsettings.json create mode 100644 Api.Localization/Dockerfile create mode 100644 Api.Localization/LICENSE create mode 100644 Api.Localization/README.md create mode 100644 Api.Localization/icon.png create mode 100644 Api.Logging.Log4Net/.docker/docker-compose.dcproj create mode 100644 Api.Logging.Log4Net/.docker/docker-compose.yml create mode 100644 Api.Logging.Log4Net/.dockerignore create mode 100644 Api.Logging.Log4Net/.github/config/slack.yml create mode 100644 Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml create mode 100644 Api.Logging.Log4Net/.gitignore create mode 100644 Api.Logging.Log4Net/.kubernetes/autoscaler.yaml create mode 100644 Api.Logging.Log4Net/.kubernetes/configmap.yaml create mode 100644 Api.Logging.Log4Net/.kubernetes/deployment.yaml create mode 100644 Api.Logging.Log4Net/.kubernetes/service.yaml create mode 100644 Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Properties/DoNotParallelize.cs create mode 100644 Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Tests.Api.Logging.Log4Net.csproj create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net.sln create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Api.Logging.Log4Net.csproj create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Controllers/ExamplesController.cs create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Program.cs create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/Properties/InternalsVisibleTo.cs create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Development.json create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Production.json create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Staging.json create mode 100644 Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.json create mode 100644 Api.Logging.Log4Net/Dockerfile create mode 100644 Api.Logging.Log4Net/LICENSE create mode 100644 Api.Logging.Log4Net/README.md create mode 100644 Api.Logging.Log4Net/icon.png create mode 100644 Api.Logging.Microsoft/.docker/docker-compose.dcproj create mode 100644 Api.Logging.Microsoft/.docker/docker-compose.yml create mode 100644 Api.Logging.Microsoft/.dockerignore create mode 100644 Api.Logging.Microsoft/.github/config/slack.yml create mode 100644 Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml create mode 100644 Api.Logging.Microsoft/.gitignore create mode 100644 Api.Logging.Microsoft/.kubernetes/autoscaler.yaml create mode 100644 Api.Logging.Microsoft/.kubernetes/configmap.yaml create mode 100644 Api.Logging.Microsoft/.kubernetes/deployment.yaml create mode 100644 Api.Logging.Microsoft/.kubernetes/service.yaml create mode 100644 Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Properties/DoNotParallelize.cs create mode 100644 Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Tests.Api.Logging.Microsoft.csproj create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft.sln create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Api.Logging.Microsoft.csproj create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Controllers/ExamplesController.cs create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Program.cs create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/Properties/InternalsVisibleTo.cs create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Development.json create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Production.json create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Staging.json create mode 100644 Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.json create mode 100644 Api.Logging.Microsoft/Dockerfile create mode 100644 Api.Logging.Microsoft/LICENSE create mode 100644 Api.Logging.Microsoft/README.md create mode 100644 Api.Logging.Microsoft/icon.png create mode 100644 Api.Logging.NLog/.docker/docker-compose.dcproj create mode 100644 Api.Logging.NLog/.docker/docker-compose.yml create mode 100644 Api.Logging.NLog/.dockerignore create mode 100644 Api.Logging.NLog/.github/config/slack.yml create mode 100644 Api.Logging.NLog/.github/workflows/build-and-deploy.yml create mode 100644 Api.Logging.NLog/.gitignore create mode 100644 Api.Logging.NLog/.kubernetes/autoscaler.yaml create mode 100644 Api.Logging.NLog/.kubernetes/configmap.yaml create mode 100644 Api.Logging.NLog/.kubernetes/deployment.yaml create mode 100644 Api.Logging.NLog/.kubernetes/service.yaml create mode 100644 Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Properties/DoNotParallelize.cs create mode 100644 Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Tests.Api.Logging.NLog.csproj create mode 100644 Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj create mode 100644 Api.Logging.NLog/Api.Logging.NLog.sln create mode 100644 Api.Logging.NLog/Api.Logging.NLog/Api.Logging.NLog.csproj create mode 100644 Api.Logging.NLog/Api.Logging.NLog/Controllers/ExamplesController.cs create mode 100644 Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local create mode 100644 Api.Logging.NLog/Api.Logging.NLog/Program.cs create mode 100644 Api.Logging.NLog/Api.Logging.NLog/Properties/InternalsVisibleTo.cs create mode 100644 Api.Logging.NLog/Api.Logging.NLog/appsettings.Development.json create mode 100644 Api.Logging.NLog/Api.Logging.NLog/appsettings.Production.json create mode 100644 Api.Logging.NLog/Api.Logging.NLog/appsettings.Staging.json create mode 100644 Api.Logging.NLog/Api.Logging.NLog/appsettings.json create mode 100644 Api.Logging.NLog/Dockerfile create mode 100644 Api.Logging.NLog/LICENSE create mode 100644 Api.Logging.NLog/README.md create mode 100644 Api.Logging.NLog/icon.png create mode 100644 Api.Logging.Serilog/.docker/docker-compose.dcproj create mode 100644 Api.Logging.Serilog/.docker/docker-compose.yml create mode 100644 Api.Logging.Serilog/.dockerignore create mode 100644 Api.Logging.Serilog/.github/config/slack.yml create mode 100644 Api.Logging.Serilog/.github/workflows/build-and-deploy.yml create mode 100644 Api.Logging.Serilog/.gitignore create mode 100644 Api.Logging.Serilog/.kubernetes/autoscaler.yaml create mode 100644 Api.Logging.Serilog/.kubernetes/configmap.yaml create mode 100644 Api.Logging.Serilog/.kubernetes/deployment.yaml create mode 100644 Api.Logging.Serilog/.kubernetes/service.yaml create mode 100644 Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Properties/DoNotParallelize.cs create mode 100644 Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Tests.Api.Logging.Serilog.csproj create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog.sln create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Api.Logging.Serilog.csproj create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Controllers/ExamplesController.cs create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Program.cs create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/Properties/InternalsVisibleTo.cs create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Development.json create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Production.json create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Staging.json create mode 100644 Api.Logging.Serilog/Api.Logging.Serilog/appsettings.json create mode 100644 Api.Logging.Serilog/Dockerfile create mode 100644 Api.Logging.Serilog/LICENSE create mode 100644 Api.Logging.Serilog/README.md create mode 100644 Api.Logging.Serilog/icon.png create mode 100644 Api.MultipartJson/.docker/docker-compose.dcproj create mode 100644 Api.MultipartJson/.docker/docker-compose.yml create mode 100644 Api.MultipartJson/.dockerignore create mode 100644 Api.MultipartJson/.github/config/slack.yml create mode 100644 Api.MultipartJson/.github/workflows/build-and-deploy.yml create mode 100644 Api.MultipartJson/.gitignore create mode 100644 Api.MultipartJson/.kubernetes/autoscaler.yaml create mode 100644 Api.MultipartJson/.kubernetes/configmap.yaml create mode 100644 Api.MultipartJson/.kubernetes/deployment.yaml create mode 100644 Api.MultipartJson/.kubernetes/service.yaml create mode 100644 Api.MultipartJson/.tests/Tests.Api.MultipartJson/Properties/DoNotParallelize.cs create mode 100644 Api.MultipartJson/.tests/Tests.Api.MultipartJson/Tests.Api.MultipartJson.csproj create mode 100644 Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj create mode 100644 Api.MultipartJson/Api.MultipartJson.sln create mode 100644 Api.MultipartJson/Api.MultipartJson/Api.MultipartJson.csproj create mode 100644 Api.MultipartJson/Api.MultipartJson/Controllers/ExamplesController.cs create mode 100644 Api.MultipartJson/Api.MultipartJson/Controllers/Requests/JsonBody.cs create mode 100644 Api.MultipartJson/Api.MultipartJson/Dockerfile.Local create mode 100644 Api.MultipartJson/Api.MultipartJson/Program.cs create mode 100644 Api.MultipartJson/Api.MultipartJson/Properties/InternalsVisibleTo.cs create mode 100644 Api.MultipartJson/Api.MultipartJson/appsettings.Development.json create mode 100644 Api.MultipartJson/Api.MultipartJson/appsettings.Production.json create mode 100644 Api.MultipartJson/Api.MultipartJson/appsettings.Staging.json create mode 100644 Api.MultipartJson/Api.MultipartJson/appsettings.json create mode 100644 Api.MultipartJson/Dockerfile create mode 100644 Api.MultipartJson/LICENSE create mode 100644 Api.MultipartJson/README.md create mode 100644 Api.MultipartJson/icon.png create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.dockerignore create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.gitignore create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-443.yaml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-80.yaml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.sln create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.csproj create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Program.cs create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Production.json create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.json create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/wwwroot/csp-violation.html create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/LICENSE create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/README.md create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/icon.png create mode 100644 Api.PolicyHeaders.ContentSecurityPolicy/localhost.pfx create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.dockerignore create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.gitignore create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Tests.Api.PolicyHeaders.ContentTypeOptions.csproj create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.sln create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.csproj create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Program.cs create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Development.json create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Production.json create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.json create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation-wrong-type.txt create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation.html create mode 100644 Api.PolicyHeaders.ContentTypeOptions/Dockerfile create mode 100644 Api.PolicyHeaders.ContentTypeOptions/LICENSE create mode 100644 Api.PolicyHeaders.ContentTypeOptions/README.md create mode 100644 Api.PolicyHeaders.ContentTypeOptions/icon.png create mode 100644 Api.PolicyHeaders.Cors/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.Cors/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.Cors/.dockerignore create mode 100644 Api.PolicyHeaders.Cors/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.Cors/.gitignore create mode 100644 Api.PolicyHeaders.Cors/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.Cors/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Tests.Api.PolicyHeaders.Cors.csproj create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.sln create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.csproj create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Program.cs create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Development.json create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Production.json create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.json create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-credetntials-not-alloweed.html create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-header-not-allowed.html create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-method-not-allowed.html create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-origin-not-allowed.html create mode 100644 Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-preflight-blocked.html create mode 100644 Api.PolicyHeaders.Cors/Dockerfile create mode 100644 Api.PolicyHeaders.Cors/LICENSE create mode 100644 Api.PolicyHeaders.Cors/README.md create mode 100644 Api.PolicyHeaders.Cors/icon.png create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.dockerignore create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.gitignore create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Tests.Api.PolicyHeaders.ForwardedHeaders.csproj create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.sln create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.csproj create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Program.cs create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Development.json create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Production.json create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.json create mode 100644 Api.PolicyHeaders.ForwardedHeaders/Dockerfile create mode 100644 Api.PolicyHeaders.ForwardedHeaders/LICENSE create mode 100644 Api.PolicyHeaders.ForwardedHeaders/README.md create mode 100644 Api.PolicyHeaders.ForwardedHeaders/icon.png create mode 100644 Api.PolicyHeaders.FrameOptions/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.FrameOptions/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.FrameOptions/.dockerignore create mode 100644 Api.PolicyHeaders.FrameOptions/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.FrameOptions/.gitignore create mode 100644 Api.PolicyHeaders.FrameOptions/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.FrameOptions/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Tests.Api.PolicyHeaders.FrameOptions.csproj create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.sln create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.csproj create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Program.cs create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Development.json create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Production.json create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.json create mode 100644 Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/wwwroot/frame-options-violation.html create mode 100644 Api.PolicyHeaders.FrameOptions/Dockerfile create mode 100644 Api.PolicyHeaders.FrameOptions/LICENSE create mode 100644 Api.PolicyHeaders.FrameOptions/README.md create mode 100644 Api.PolicyHeaders.FrameOptions/icon.png create mode 100644 Api.PolicyHeaders.Hsts/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.Hsts/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.Hsts/.dockerignore create mode 100644 Api.PolicyHeaders.Hsts/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.Hsts/.gitignore create mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/httproute-443.yaml create mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/httproute-80.yaml create mode 100644 Api.PolicyHeaders.Hsts/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Tests.Api.PolicyHeaders.Hsts.csproj create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.sln create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.csproj create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Program.cs create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Development.json create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Production.json create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.json create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/wwwroot/hsts-violation-image.png create mode 100644 Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/wwwroot/hsts-violation.html create mode 100644 Api.PolicyHeaders.Hsts/Dockerfile create mode 100644 Api.PolicyHeaders.Hsts/LICENSE create mode 100644 Api.PolicyHeaders.Hsts/README.md create mode 100644 Api.PolicyHeaders.Hsts/icon.png create mode 100644 Api.PolicyHeaders.Hsts/localhost.pfx create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.dockerignore create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.gitignore create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Tests.Api.PolicyHeaders.ReferrerPolicy.csproj create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.sln create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.csproj create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Program.cs create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Development.json create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Production.json create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.json create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/wwwroot/referrer-policy-violation.html create mode 100644 Api.PolicyHeaders.ReferrerPolicy/Dockerfile create mode 100644 Api.PolicyHeaders.ReferrerPolicy/LICENSE create mode 100644 Api.PolicyHeaders.ReferrerPolicy/README.md create mode 100644 Api.PolicyHeaders.ReferrerPolicy/icon.png create mode 100644 Api.PolicyHeaders.Robots/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.Robots/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.Robots/.dockerignore create mode 100644 Api.PolicyHeaders.Robots/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.Robots/.gitignore create mode 100644 Api.PolicyHeaders.Robots/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.Robots/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Tests.Api.PolicyHeaders.Robots.csproj create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.sln create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.csproj create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Program.cs create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Development.json create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Production.json create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.json create mode 100644 Api.PolicyHeaders.Robots/Dockerfile create mode 100644 Api.PolicyHeaders.Robots/LICENSE create mode 100644 Api.PolicyHeaders.Robots/README.md create mode 100644 Api.PolicyHeaders.Robots/icon.png create mode 100644 Api.PolicyHeaders.XssProtection/.docker/docker-compose.dcproj create mode 100644 Api.PolicyHeaders.XssProtection/.docker/docker-compose.yml create mode 100644 Api.PolicyHeaders.XssProtection/.dockerignore create mode 100644 Api.PolicyHeaders.XssProtection/.github/config/slack.yml create mode 100644 Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml create mode 100644 Api.PolicyHeaders.XssProtection/.gitignore create mode 100644 Api.PolicyHeaders.XssProtection/.kubernetes/autoscaler.yaml create mode 100644 Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml create mode 100644 Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml create mode 100644 Api.PolicyHeaders.XssProtection/.kubernetes/service.yaml create mode 100644 Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Properties/DoNotParallelize.cs create mode 100644 Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Tests.Api.PolicyHeaders.XssProtection.csproj create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.sln create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.csproj create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Controllers/ExamplesController.cs create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Program.cs create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Properties/InternalsVisibleTo.cs create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Development.json create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Production.json create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Staging.json create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.json create mode 100644 Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/wwwroot/xss-violation.html create mode 100644 Api.PolicyHeaders.XssProtection/Dockerfile create mode 100644 Api.PolicyHeaders.XssProtection/LICENSE create mode 100644 Api.PolicyHeaders.XssProtection/README.md create mode 100644 Api.PolicyHeaders.XssProtection/icon.png create mode 100644 Api.RequestTracing/.docker/docker-compose.dcproj create mode 100644 Api.RequestTracing/.docker/docker-compose.yml create mode 100644 Api.RequestTracing/.dockerignore create mode 100644 Api.RequestTracing/.github/config/slack.yml create mode 100644 Api.RequestTracing/.github/workflows/build-and-deploy.yml create mode 100644 Api.RequestTracing/.gitignore create mode 100644 Api.RequestTracing/.kubernetes/autoscaler.yaml create mode 100644 Api.RequestTracing/.kubernetes/configmap.yaml create mode 100644 Api.RequestTracing/.kubernetes/deployment.yaml create mode 100644 Api.RequestTracing/.kubernetes/service.yaml create mode 100644 Api.RequestTracing/.tests/Tests.Api.RequestTracing/Properties/DoNotParallelize.cs create mode 100644 Api.RequestTracing/.tests/Tests.Api.RequestTracing/Tests.Api.RequestTracing.csproj create mode 100644 Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj create mode 100644 Api.RequestTracing/Api.RequestTracing.sln create mode 100644 Api.RequestTracing/Api.RequestTracing/Api.RequestTracing.csproj create mode 100644 Api.RequestTracing/Api.RequestTracing/Controllers/ExamplesController.cs create mode 100644 Api.RequestTracing/Api.RequestTracing/Dockerfile.Local create mode 100644 Api.RequestTracing/Api.RequestTracing/Program.cs create mode 100644 Api.RequestTracing/Api.RequestTracing/Properties/InternalsVisibleTo.cs create mode 100644 Api.RequestTracing/Api.RequestTracing/appsettings.Development.json create mode 100644 Api.RequestTracing/Api.RequestTracing/appsettings.Production.json create mode 100644 Api.RequestTracing/Api.RequestTracing/appsettings.Staging.json create mode 100644 Api.RequestTracing/Api.RequestTracing/appsettings.json create mode 100644 Api.RequestTracing/Dockerfile create mode 100644 Api.RequestTracing/LICENSE create mode 100644 Api.RequestTracing/README.md create mode 100644 Api.RequestTracing/icon.png create mode 100644 Api.ResponseCache/.docker/docker-compose.dcproj create mode 100644 Api.ResponseCache/.docker/docker-compose.yml create mode 100644 Api.ResponseCache/.dockerignore create mode 100644 Api.ResponseCache/.github/config/slack.yml create mode 100644 Api.ResponseCache/.github/workflows/build-and-deploy.yml create mode 100644 Api.ResponseCache/.gitignore create mode 100644 Api.ResponseCache/.kubernetes/autoscaler.yaml create mode 100644 Api.ResponseCache/.kubernetes/configmap.yaml create mode 100644 Api.ResponseCache/.kubernetes/deployment.yaml create mode 100644 Api.ResponseCache/.kubernetes/service.yaml create mode 100644 Api.ResponseCache/.tests/Tests.Api.ResponseCache/Properties/DoNotParallelize.cs create mode 100644 Api.ResponseCache/.tests/Tests.Api.ResponseCache/Tests.Api.ResponseCache.csproj create mode 100644 Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj create mode 100644 Api.ResponseCache/Api.ResponseCache.sln create mode 100644 Api.ResponseCache/Api.ResponseCache/Api.ResponseCache.csproj create mode 100644 Api.ResponseCache/Api.ResponseCache/Controllers/ExamplesController.cs create mode 100644 Api.ResponseCache/Api.ResponseCache/Dockerfile.Local create mode 100644 Api.ResponseCache/Api.ResponseCache/Program.cs create mode 100644 Api.ResponseCache/Api.ResponseCache/Properties/InternalsVisibleTo.cs create mode 100644 Api.ResponseCache/Api.ResponseCache/appsettings.Development.json create mode 100644 Api.ResponseCache/Api.ResponseCache/appsettings.Production.json create mode 100644 Api.ResponseCache/Api.ResponseCache/appsettings.Staging.json create mode 100644 Api.ResponseCache/Api.ResponseCache/appsettings.json create mode 100644 Api.ResponseCache/Dockerfile create mode 100644 Api.ResponseCache/LICENSE create mode 100644 Api.ResponseCache/README.md create mode 100644 Api.ResponseCache/icon.png create mode 100644 Api.ResponseCompression/.docker/docker-compose.dcproj create mode 100644 Api.ResponseCompression/.docker/docker-compose.yml create mode 100644 Api.ResponseCompression/.dockerignore create mode 100644 Api.ResponseCompression/.github/config/slack.yml create mode 100644 Api.ResponseCompression/.github/workflows/build-and-deploy.yml create mode 100644 Api.ResponseCompression/.gitignore create mode 100644 Api.ResponseCompression/.kubernetes/autoscaler.yaml create mode 100644 Api.ResponseCompression/.kubernetes/configmap.yaml create mode 100644 Api.ResponseCompression/.kubernetes/deployment.yaml create mode 100644 Api.ResponseCompression/.kubernetes/service.yaml create mode 100644 Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Properties/DoNotParallelize.cs create mode 100644 Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Tests.Api.ResponseCompression.csproj create mode 100644 Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj create mode 100644 Api.ResponseCompression/Api.ResponseCompression.sln create mode 100644 Api.ResponseCompression/Api.ResponseCompression/Api.ResponseCompression.csproj create mode 100644 Api.ResponseCompression/Api.ResponseCompression/Controllers/ExamplesController.cs create mode 100644 Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local create mode 100644 Api.ResponseCompression/Api.ResponseCompression/Program.cs create mode 100644 Api.ResponseCompression/Api.ResponseCompression/Properties/InternalsVisibleTo.cs create mode 100644 Api.ResponseCompression/Api.ResponseCompression/appsettings.Development.json create mode 100644 Api.ResponseCompression/Api.ResponseCompression/appsettings.Production.json create mode 100644 Api.ResponseCompression/Api.ResponseCompression/appsettings.Staging.json create mode 100644 Api.ResponseCompression/Api.ResponseCompression/appsettings.json create mode 100644 Api.ResponseCompression/Dockerfile create mode 100644 Api.ResponseCompression/LICENSE create mode 100644 Api.ResponseCompression/README.md create mode 100644 Api.ResponseCompression/icon.png create mode 100644 Api.Session/.docker/docker-compose.dcproj create mode 100644 Api.Session/.docker/docker-compose.yml create mode 100644 Api.Session/.dockerignore create mode 100644 Api.Session/.github/config/slack.yml create mode 100644 Api.Session/.github/workflows/build-and-deploy.yml create mode 100644 Api.Session/.gitignore create mode 100644 Api.Session/.kubernetes/autoscaler.yaml create mode 100644 Api.Session/.kubernetes/configmap.yaml create mode 100644 Api.Session/.kubernetes/deployment.yaml create mode 100644 Api.Session/.kubernetes/service.yaml create mode 100644 Api.Session/.tests/Tests.Api.Session/Properties/DoNotParallelize.cs create mode 100644 Api.Session/.tests/Tests.Api.Session/Tests.Api.Session.csproj create mode 100644 Api.Session/Api.Session.Models/Api.Session.Models.csproj create mode 100644 Api.Session/Api.Session.sln create mode 100644 Api.Session/Api.Session/Api.Session.csproj create mode 100644 Api.Session/Api.Session/Controllers/ExamplesController.cs create mode 100644 Api.Session/Api.Session/Dockerfile.Local create mode 100644 Api.Session/Api.Session/Program.cs create mode 100644 Api.Session/Api.Session/Properties/InternalsVisibleTo.cs create mode 100644 Api.Session/Api.Session/appsettings.Development.json create mode 100644 Api.Session/Api.Session/appsettings.Production.json create mode 100644 Api.Session/Api.Session/appsettings.Staging.json create mode 100644 Api.Session/Api.Session/appsettings.json create mode 100644 Api.Session/Dockerfile create mode 100644 Api.Session/LICENSE create mode 100644 Api.Session/README.md create mode 100644 Api.Session/icon.png create mode 100644 Api.StartupTasks/.docker/docker-compose.dcproj create mode 100644 Api.StartupTasks/.docker/docker-compose.yml create mode 100644 Api.StartupTasks/.dockerignore create mode 100644 Api.StartupTasks/.github/config/slack.yml create mode 100644 Api.StartupTasks/.github/workflows/build-and-deploy.yml create mode 100644 Api.StartupTasks/.gitignore create mode 100644 Api.StartupTasks/.kubernetes/autoscaler.yaml create mode 100644 Api.StartupTasks/.kubernetes/configmap.yaml create mode 100644 Api.StartupTasks/.kubernetes/deployment.yaml create mode 100644 Api.StartupTasks/.kubernetes/service.yaml create mode 100644 Api.StartupTasks/.tests/Tests.Api.StartupTasks/Properties/DoNotParallelize.cs create mode 100644 Api.StartupTasks/.tests/Tests.Api.StartupTasks/Tests.Api.StartupTasks.csproj create mode 100644 Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj create mode 100644 Api.StartupTasks/Api.StartupTasks.sln create mode 100644 Api.StartupTasks/Api.StartupTasks/Api.StartupTasks.csproj create mode 100644 Api.StartupTasks/Api.StartupTasks/Controllers/ExamplesController.cs create mode 100644 Api.StartupTasks/Api.StartupTasks/Dockerfile.Local create mode 100644 Api.StartupTasks/Api.StartupTasks/Program.cs create mode 100644 Api.StartupTasks/Api.StartupTasks/Properties/InternalsVisibleTo.cs create mode 100644 Api.StartupTasks/Api.StartupTasks/Startup/ExampleStartupTask.cs create mode 100644 Api.StartupTasks/Api.StartupTasks/appsettings.Development.json create mode 100644 Api.StartupTasks/Api.StartupTasks/appsettings.Production.json create mode 100644 Api.StartupTasks/Api.StartupTasks/appsettings.Staging.json create mode 100644 Api.StartupTasks/Api.StartupTasks/appsettings.json create mode 100644 Api.StartupTasks/Dockerfile create mode 100644 Api.StartupTasks/LICENSE create mode 100644 Api.StartupTasks/README.md create mode 100644 Api.StartupTasks/icon.png create mode 100644 Api.StaticFiles/.docker/docker-compose.dcproj create mode 100644 Api.StaticFiles/.docker/docker-compose.yml create mode 100644 Api.StaticFiles/.dockerignore create mode 100644 Api.StaticFiles/.github/config/slack.yml create mode 100644 Api.StaticFiles/.github/workflows/build-and-deploy.yml create mode 100644 Api.StaticFiles/.gitignore create mode 100644 Api.StaticFiles/.kubernetes/autoscaler.yaml create mode 100644 Api.StaticFiles/.kubernetes/configmap.yaml create mode 100644 Api.StaticFiles/.kubernetes/deployment.yaml create mode 100644 Api.StaticFiles/.kubernetes/service.yaml create mode 100644 Api.StaticFiles/.tests/Tests.Api.StaticFiles/Properties/DoNotParallelize.cs create mode 100644 Api.StaticFiles/.tests/Tests.Api.StaticFiles/Tests.Api.StaticFiles.csproj create mode 100644 Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj create mode 100644 Api.StaticFiles/Api.StaticFiles.sln create mode 100644 Api.StaticFiles/Api.StaticFiles/Api.StaticFiles.csproj create mode 100644 Api.StaticFiles/Api.StaticFiles/Controllers/ExamplesController.cs create mode 100644 Api.StaticFiles/Api.StaticFiles/Dockerfile.Local create mode 100644 Api.StaticFiles/Api.StaticFiles/Program.cs create mode 100644 Api.StaticFiles/Api.StaticFiles/Properties/InternalsVisibleTo.cs create mode 100644 Api.StaticFiles/Api.StaticFiles/appsettings.Development.json create mode 100644 Api.StaticFiles/Api.StaticFiles/appsettings.Production.json create mode 100644 Api.StaticFiles/Api.StaticFiles/appsettings.Staging.json create mode 100644 Api.StaticFiles/Api.StaticFiles/appsettings.json create mode 100644 Api.StaticFiles/Api.StaticFiles/wwwroot/fonts/open-sans-v44-latin-regular.woff2 create mode 100644 Api.StaticFiles/Api.StaticFiles/wwwroot/images/image.jpg create mode 100644 Api.StaticFiles/Api.StaticFiles/wwwroot/index.html create mode 100644 Api.StaticFiles/Dockerfile create mode 100644 Api.StaticFiles/LICENSE create mode 100644 Api.StaticFiles/README.md create mode 100644 Api.StaticFiles/icon.png create mode 100644 Api.Storage.Azure/.docker/docker-compose.dcproj create mode 100644 Api.Storage.Azure/.docker/docker-compose.yml create mode 100644 Api.Storage.Azure/.dockerignore create mode 100644 Api.Storage.Azure/.github/config/slack.yml create mode 100644 Api.Storage.Azure/.github/workflows/build-and-deploy.yml create mode 100644 Api.Storage.Azure/.gitignore create mode 100644 Api.Storage.Azure/.kubernetes/autoscaler.yaml create mode 100644 Api.Storage.Azure/.kubernetes/configmap.yaml create mode 100644 Api.Storage.Azure/.kubernetes/deployment.yaml create mode 100644 Api.Storage.Azure/.kubernetes/service.yaml create mode 100644 Api.Storage.Azure/.kubernetes/storage-pv.yaml create mode 100644 Api.Storage.Azure/.kubernetes/storage-pvc.yaml create mode 100644 Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Properties/DoNotParallelize.cs create mode 100644 Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Tests.Api.Storage.Azure.csproj create mode 100644 Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj create mode 100644 Api.Storage.Azure/Api.Storage.Azure.sln create mode 100644 Api.Storage.Azure/Api.Storage.Azure/Api.Storage.Azure.csproj create mode 100644 Api.Storage.Azure/Api.Storage.Azure/Controllers/ExamplesController.cs create mode 100644 Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local create mode 100644 Api.Storage.Azure/Api.Storage.Azure/Program.cs create mode 100644 Api.Storage.Azure/Api.Storage.Azure/Properties/InternalsVisibleTo.cs create mode 100644 Api.Storage.Azure/Api.Storage.Azure/appsettings.Development.json create mode 100644 Api.Storage.Azure/Api.Storage.Azure/appsettings.Production.json create mode 100644 Api.Storage.Azure/Api.Storage.Azure/appsettings.Staging.json create mode 100644 Api.Storage.Azure/Api.Storage.Azure/appsettings.json create mode 100644 Api.Storage.Azure/Dockerfile create mode 100644 Api.Storage.Azure/LICENSE create mode 100644 Api.Storage.Azure/README.md create mode 100644 Api.Storage.Azure/icon.png create mode 100644 Api.Storage.Local/.docker/docker-compose.dcproj create mode 100644 Api.Storage.Local/.docker/docker-compose.yml create mode 100644 Api.Storage.Local/.dockerignore create mode 100644 Api.Storage.Local/.github/config/slack.yml create mode 100644 Api.Storage.Local/.github/workflows/build-and-deploy.yml create mode 100644 Api.Storage.Local/.gitignore create mode 100644 Api.Storage.Local/.kubernetes/autoscaler.yaml create mode 100644 Api.Storage.Local/.kubernetes/configmap.yaml create mode 100644 Api.Storage.Local/.kubernetes/deployment.yaml create mode 100644 Api.Storage.Local/.kubernetes/service.yaml create mode 100644 Api.Storage.Local/.kubernetes/storage-pvc.yaml create mode 100644 Api.Storage.Local/.kubernetes/storage-storageclass.yaml create mode 100644 Api.Storage.Local/.tests/Tests.Api.Storage.Local/Properties/DoNotParallelize.cs create mode 100644 Api.Storage.Local/.tests/Tests.Api.Storage.Local/Tests.Api.Storage.Local.csproj create mode 100644 Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj create mode 100644 Api.Storage.Local/Api.Storage.Local.sln create mode 100644 Api.Storage.Local/Api.Storage.Local/Api.Storage.Local.csproj create mode 100644 Api.Storage.Local/Api.Storage.Local/Controllers/ExamplesController.cs create mode 100644 Api.Storage.Local/Api.Storage.Local/Dockerfile.Local create mode 100644 Api.Storage.Local/Api.Storage.Local/Program.cs create mode 100644 Api.Storage.Local/Api.Storage.Local/Properties/InternalsVisibleTo.cs create mode 100644 Api.Storage.Local/Api.Storage.Local/appsettings.Development.json create mode 100644 Api.Storage.Local/Api.Storage.Local/appsettings.Production.json create mode 100644 Api.Storage.Local/Api.Storage.Local/appsettings.Staging.json create mode 100644 Api.Storage.Local/Api.Storage.Local/appsettings.json create mode 100644 Api.Storage.Local/Dockerfile create mode 100644 Api.Storage.Local/LICENSE create mode 100644 Api.Storage.Local/README.md create mode 100644 Api.Storage.Local/icon.png create mode 100644 Api.TimeZone/.docker/docker-compose.dcproj create mode 100644 Api.TimeZone/.docker/docker-compose.yml create mode 100644 Api.TimeZone/.dockerignore create mode 100644 Api.TimeZone/.github/config/slack.yml create mode 100644 Api.TimeZone/.github/workflows/build-and-deploy.yml create mode 100644 Api.TimeZone/.gitignore create mode 100644 Api.TimeZone/.kubernetes/autoscaler.yaml create mode 100644 Api.TimeZone/.kubernetes/configmap.yaml create mode 100644 Api.TimeZone/.kubernetes/deployment.yaml create mode 100644 Api.TimeZone/.kubernetes/service.yaml create mode 100644 Api.TimeZone/.tests/Tests.Api.TimeZone/Properties/DoNotParallelize.cs create mode 100644 Api.TimeZone/.tests/Tests.Api.TimeZone/Tests.Api.TimeZone.csproj create mode 100644 Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj create mode 100644 Api.TimeZone/Api.TimeZone.sln create mode 100644 Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj create mode 100644 Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs create mode 100644 Api.TimeZone/Api.TimeZone/Controllers/Requests/DateTimeRequest.cs create mode 100644 Api.TimeZone/Api.TimeZone/Dockerfile.Local create mode 100644 Api.TimeZone/Api.TimeZone/Program.cs create mode 100644 Api.TimeZone/Api.TimeZone/Properties/InternalsVisibleTo.cs create mode 100644 Api.TimeZone/Api.TimeZone/appsettings.Development.json create mode 100644 Api.TimeZone/Api.TimeZone/appsettings.Production.json create mode 100644 Api.TimeZone/Api.TimeZone/appsettings.Staging.json create mode 100644 Api.TimeZone/Api.TimeZone/appsettings.json create mode 100644 Api.TimeZone/Dockerfile create mode 100644 Api.TimeZone/LICENSE create mode 100644 Api.TimeZone/README.md create mode 100644 Api.TimeZone/icon.png create mode 100644 Api.Versioning/.docker/docker-compose.dcproj create mode 100644 Api.Versioning/.docker/docker-compose.yml create mode 100644 Api.Versioning/.dockerignore create mode 100644 Api.Versioning/.github/config/slack.yml create mode 100644 Api.Versioning/.github/workflows/build-and-deploy.yml create mode 100644 Api.Versioning/.gitignore create mode 100644 Api.Versioning/.kubernetes/autoscaler.yaml create mode 100644 Api.Versioning/.kubernetes/configmap.yaml create mode 100644 Api.Versioning/.kubernetes/deployment.yaml create mode 100644 Api.Versioning/.kubernetes/service.yaml create mode 100644 Api.Versioning/.tests/Tests.Api.Versioning/Properties/DoNotParallelize.cs create mode 100644 Api.Versioning/.tests/Tests.Api.Versioning/Tests.Api.Versioning.csproj create mode 100644 Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj create mode 100644 Api.Versioning/Api.Versioning.sln create mode 100644 Api.Versioning/Api.Versioning/Api.Versioning.csproj create mode 100644 Api.Versioning/Api.Versioning/Controllers/ExamplesController.cs create mode 100644 Api.Versioning/Api.Versioning/Dockerfile.Local create mode 100644 Api.Versioning/Api.Versioning/Program.cs create mode 100644 Api.Versioning/Api.Versioning/Properties/InternalsVisibleTo.cs create mode 100644 Api.Versioning/Api.Versioning/appsettings.Development.json create mode 100644 Api.Versioning/Api.Versioning/appsettings.Production.json create mode 100644 Api.Versioning/Api.Versioning/appsettings.Staging.json create mode 100644 Api.Versioning/Api.Versioning/appsettings.json create mode 100644 Api.Versioning/Dockerfile create mode 100644 Api.Versioning/LICENSE create mode 100644 Api.Versioning/README.md create mode 100644 Api.Versioning/icon.png create mode 100644 Api.VirusScan/.docker/docker-compose.dcproj create mode 100644 Api.VirusScan/.docker/docker-compose.yml create mode 100644 Api.VirusScan/.dockerignore create mode 100644 Api.VirusScan/.github/config/slack.yml create mode 100644 Api.VirusScan/.github/workflows/build-and-deploy.yml create mode 100644 Api.VirusScan/.gitignore create mode 100644 Api.VirusScan/.kubernetes/autoscaler.yaml create mode 100644 Api.VirusScan/.kubernetes/configmap.yaml create mode 100644 Api.VirusScan/.kubernetes/deployment.yaml create mode 100644 Api.VirusScan/.kubernetes/service.yaml create mode 100644 Api.VirusScan/.tests/Tests.Api.VirusScan/Properties/DoNotParallelize.cs create mode 100644 Api.VirusScan/.tests/Tests.Api.VirusScan/Tests.Api.VirusScan.csproj create mode 100644 Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj create mode 100644 Api.VirusScan/Api.VirusScan.sln create mode 100644 Api.VirusScan/Api.VirusScan/Api.VirusScan.csproj create mode 100644 Api.VirusScan/Api.VirusScan/Controllers/ExamplesController.cs create mode 100644 Api.VirusScan/Api.VirusScan/Dockerfile.Local create mode 100644 Api.VirusScan/Api.VirusScan/Program.cs create mode 100644 Api.VirusScan/Api.VirusScan/Properties/InternalsVisibleTo.cs create mode 100644 Api.VirusScan/Api.VirusScan/appsettings.Development.json create mode 100644 Api.VirusScan/Api.VirusScan/appsettings.Production.json create mode 100644 Api.VirusScan/Api.VirusScan/appsettings.Staging.json create mode 100644 Api.VirusScan/Api.VirusScan/appsettings.json create mode 100644 Api.VirusScan/Dockerfile create mode 100644 Api.VirusScan/LICENSE create mode 100644 Api.VirusScan/README.md create mode 100644 Api.VirusScan/icon.png create mode 100644 Api._Blank/.docker/docker-compose.dcproj create mode 100644 Api._Blank/.docker/docker-compose.yml create mode 100644 Api._Blank/.dockerignore create mode 100644 Api._Blank/.github/config/slack.yml create mode 100644 Api._Blank/.github/workflows/build-and-deploy.yml create mode 100644 Api._Blank/.gitignore create mode 100644 Api._Blank/.kubernetes/autoscaler.yaml create mode 100644 Api._Blank/.kubernetes/configmap.yaml create mode 100644 Api._Blank/.kubernetes/deployment.yaml create mode 100644 Api._Blank/.kubernetes/service.yaml create mode 100644 Api._Blank/.tests/Tests.Api.Blank/Properties/DoNotParallelize.cs create mode 100644 Api._Blank/.tests/Tests.Api.Blank/Tests.Api.Blank.csproj create mode 100644 Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj create mode 100644 Api._Blank/Api.Blank.sln create mode 100644 Api._Blank/Api.Blank/Api.Blank.csproj create mode 100644 Api._Blank/Api.Blank/Dockerfile.Local create mode 100644 Api._Blank/Api.Blank/Program.cs create mode 100644 Api._Blank/Api.Blank/Properties/InternalsVisibleTo.cs create mode 100644 Api._Blank/Api.Blank/appsettings.Development.json create mode 100644 Api._Blank/Api.Blank/appsettings.Production.json create mode 100644 Api._Blank/Api.Blank/appsettings.Staging.json create mode 100644 Api._Blank/Api.Blank/appsettings.json create mode 100644 Api._Blank/Dockerfile create mode 100644 Api._Blank/LICENSE create mode 100644 Api._Blank/README.md create mode 100644 Api._Blank/icon.png create mode 100644 Console.CustomConfigSection/.docker/docker-compose.dcproj create mode 100644 Console.CustomConfigSection/.docker/docker-compose.yml create mode 100644 Console.CustomConfigSection/.dockerignore create mode 100644 Console.CustomConfigSection/.github/config/slack.yml create mode 100644 Console.CustomConfigSection/.github/workflows/build-and-deploy.yml create mode 100644 Console.CustomConfigSection/.gitignore create mode 100644 Console.CustomConfigSection/.kubernetes/configmap.yaml create mode 100644 Console.CustomConfigSection/.kubernetes/cronjob.yaml create mode 100644 Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Properties/DoNotParallelize.cs create mode 100644 Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Tests.Console.CustomConfigSection.csproj create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection.sln create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Config/CustomOptions.cs create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Console.CustomConfigSection.csproj create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Dockerfile.Local create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Program.cs create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Properties/InternalsVisibleTo.cs create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/Workers/ExampleWorker.cs create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Development.json create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Production.json create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Staging.json create mode 100644 Console.CustomConfigSection/Console.CustomConfigSection/appsettings.json create mode 100644 Console.CustomConfigSection/Dockerfile create mode 100644 Console.CustomConfigSection/README.md create mode 100644 Console.CustomService/.docker/docker-compose.dcproj create mode 100644 Console.CustomService/.docker/docker-compose.yml create mode 100644 Console.CustomService/.dockerignore create mode 100644 Console.CustomService/.github/config/slack.yml create mode 100644 Console.CustomService/.github/workflows/build-and-deploy.yml create mode 100644 Console.CustomService/.gitignore create mode 100644 Console.CustomService/.kubernetes/configmap.yaml create mode 100644 Console.CustomService/.kubernetes/cronjob.yaml create mode 100644 Console.CustomService/.tests/Tests.Console.CustomService/Properties/DoNotParallelize.cs create mode 100644 Console.CustomService/.tests/Tests.Console.CustomService/Tests.Console.CustomService.csproj create mode 100644 Console.CustomService/Console.CustomService.sln create mode 100644 Console.CustomService/Console.CustomService/Console.CustomService.csproj create mode 100644 Console.CustomService/Console.CustomService/Dockerfile.Local create mode 100644 Console.CustomService/Console.CustomService/Program.cs create mode 100644 Console.CustomService/Console.CustomService/Properties/InternalsVisibleTo.cs create mode 100644 Console.CustomService/Console.CustomService/Services/Abstractions/IExampleService.cs create mode 100644 Console.CustomService/Console.CustomService/Services/ExampleService.cs create mode 100644 Console.CustomService/Console.CustomService/Workers/ExampleWorker.cs create mode 100644 Console.CustomService/Console.CustomService/appsettings.Development.json create mode 100644 Console.CustomService/Console.CustomService/appsettings.Production.json create mode 100644 Console.CustomService/Console.CustomService/appsettings.Staging.json create mode 100644 Console.CustomService/Console.CustomService/appsettings.json create mode 100644 Console.CustomService/Dockerfile create mode 100644 Console.CustomService/README.md create mode 100644 Console.Data.InMemory/.docker/docker-compose.dcproj create mode 100644 Console.Data.InMemory/.docker/docker-compose.yml create mode 100644 Console.Data.InMemory/.dockerignore create mode 100644 Console.Data.InMemory/.github/config/slack.yml create mode 100644 Console.Data.InMemory/.github/workflows/build-and-deploy.yml create mode 100644 Console.Data.InMemory/.gitignore create mode 100644 Console.Data.InMemory/.kubernetes/configmap.yaml create mode 100644 Console.Data.InMemory/.kubernetes/cronjob.yaml create mode 100644 Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Properties/DoNotParallelize.cs create mode 100644 Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Tests.Console.Data.InMemory.csproj create mode 100644 Console.Data.InMemory/Console.Data.InMemory.sln create mode 100644 Console.Data.InMemory/Console.Data.InMemory/Console.Data.InMemory.csproj create mode 100644 Console.Data.InMemory/Console.Data.InMemory/Data/InMemoryDbContext.cs create mode 100644 Console.Data.InMemory/Console.Data.InMemory/Data/Mappings/ExampleMapping.cs create mode 100644 Console.Data.InMemory/Console.Data.InMemory/Data/Models/Example.cs create mode 100644 Console.Data.InMemory/Console.Data.InMemory/Dockerfile.Local create mode 100644 Console.Data.InMemory/Console.Data.InMemory/Program.cs create mode 100644 Console.Data.InMemory/Console.Data.InMemory/Properties/InternalsVisibleTo.cs create mode 100644 Console.Data.InMemory/Console.Data.InMemory/Workers/ExampleWorker.cs create mode 100644 Console.Data.InMemory/Console.Data.InMemory/appsettings.Development.json create mode 100644 Console.Data.InMemory/Console.Data.InMemory/appsettings.Production.json create mode 100644 Console.Data.InMemory/Console.Data.InMemory/appsettings.Staging.json create mode 100644 Console.Data.InMemory/Console.Data.InMemory/appsettings.json create mode 100644 Console.Data.InMemory/Dockerfile create mode 100644 Console.Data.InMemory/README.md create mode 100644 Console.Data.MySql/.docker/docker-compose.dcproj create mode 100644 Console.Data.MySql/.docker/docker-compose.yml create mode 100644 Console.Data.MySql/.dockerignore create mode 100644 Console.Data.MySql/.github/config/slack.yml create mode 100644 Console.Data.MySql/.github/workflows/build-and-deploy.yml create mode 100644 Console.Data.MySql/.gitignore create mode 100644 Console.Data.MySql/.kubernetes/auth-sql-secret.yaml create mode 100644 Console.Data.MySql/.kubernetes/configmap.yaml create mode 100644 Console.Data.MySql/.kubernetes/cronjob.yaml create mode 100644 Console.Data.MySql/.tests/Tests.Console.Data.MySql/Properties/DoNotParallelize.cs create mode 100644 Console.Data.MySql/.tests/Tests.Console.Data.MySql/Tests.Console.Data.MySql.csproj create mode 100644 Console.Data.MySql/Console.Data.MySql.sln create mode 100644 Console.Data.MySql/Console.Data.MySql/Console.Data.MySql.csproj create mode 100644 Console.Data.MySql/Console.Data.MySql/Data/Mappings/ExampleMapping.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Data/Models/Example.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContext.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContextFactory.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Dockerfile.Local create mode 100644 Console.Data.MySql/Console.Data.MySql/Migrations/20260425173327_Initial.Designer.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Migrations/20260425173327_Initial.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Program.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Properties/InternalsVisibleTo.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/Workers/ExampleWorker.cs create mode 100644 Console.Data.MySql/Console.Data.MySql/appsettings.Development.json create mode 100644 Console.Data.MySql/Console.Data.MySql/appsettings.Production.json create mode 100644 Console.Data.MySql/Console.Data.MySql/appsettings.Staging.json create mode 100644 Console.Data.MySql/Console.Data.MySql/appsettings.json create mode 100644 Console.Data.MySql/Dockerfile create mode 100644 Console.Data.MySql/README.md create mode 100644 Console.Data.PostgreSQL/.docker/docker-compose.dcproj create mode 100644 Console.Data.PostgreSQL/.docker/docker-compose.yml create mode 100644 Console.Data.PostgreSQL/.dockerignore create mode 100644 Console.Data.PostgreSQL/.github/config/slack.yml create mode 100644 Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml create mode 100644 Console.Data.PostgreSQL/.gitignore create mode 100644 Console.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml create mode 100644 Console.Data.PostgreSQL/.kubernetes/configmap.yaml create mode 100644 Console.Data.PostgreSQL/.kubernetes/cronjob.yaml create mode 100644 Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Properties/DoNotParallelize.cs create mode 100644 Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Tests.Console.Data.PostgreSQL.csproj create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL.sln create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Console.Data.PostgreSQL.csproj create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Models/Example.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContext.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Dockerfile.Local create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260425181509_Initial.Designer.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260425181509_Initial.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Properties/InternalsVisibleTo.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/Workers/ExampleWorker.cs create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Development.json create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Production.json create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Staging.json create mode 100644 Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.json create mode 100644 Console.Data.PostgreSQL/Dockerfile create mode 100644 Console.Data.PostgreSQL/README.md create mode 100644 Console.Data.SqLite/.docker/docker-compose.dcproj create mode 100644 Console.Data.SqLite/.docker/docker-compose.yml create mode 100644 Console.Data.SqLite/.dockerignore create mode 100644 Console.Data.SqLite/.github/config/slack.yml create mode 100644 Console.Data.SqLite/.github/workflows/build-and-deploy.yml create mode 100644 Console.Data.SqLite/.gitignore create mode 100644 Console.Data.SqLite/.kubernetes/configmap.yaml create mode 100644 Console.Data.SqLite/.kubernetes/cronjob.yaml create mode 100644 Console.Data.SqLite/.kubernetes/data-pvc.yaml create mode 100644 Console.Data.SqLite/.kubernetes/data-storageclass.yaml create mode 100644 Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Properties/DoNotParallelize.cs create mode 100644 Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Tests.Console.Data.SqLite.csproj create mode 100644 Console.Data.SqLite/Console.Data.SqLite.sln create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Console.Data.SqLite.csproj create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Data/Mappings/ExampleMapping.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Data/Models/Example.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContext.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContextFactory.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Dockerfile.Local create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Migrations/20260427110613_Initial.Designer.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Migrations/20260427110613_Initial.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Program.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Properties/InternalsVisibleTo.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/Workers/ExampleWorker.cs create mode 100644 Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json create mode 100644 Console.Data.SqLite/Console.Data.SqLite/appsettings.Production.json create mode 100644 Console.Data.SqLite/Console.Data.SqLite/appsettings.Staging.json create mode 100644 Console.Data.SqLite/Console.Data.SqLite/appsettings.json create mode 100644 Console.Data.SqLite/Dockerfile create mode 100644 Console.Data.SqLite/README.md create mode 100644 Console.Data.SqlServer/.docker/docker-compose.dcproj create mode 100644 Console.Data.SqlServer/.docker/docker-compose.yml create mode 100644 Console.Data.SqlServer/.dockerignore create mode 100644 Console.Data.SqlServer/.github/config/slack.yml create mode 100644 Console.Data.SqlServer/.github/workflows/build-and-deploy.yml create mode 100644 Console.Data.SqlServer/.gitignore create mode 100644 Console.Data.SqlServer/.kubernetes/auth-sql-secret.yaml create mode 100644 Console.Data.SqlServer/.kubernetes/configmap.yaml create mode 100644 Console.Data.SqlServer/.kubernetes/cronjob.yaml create mode 100644 Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Properties/DoNotParallelize.cs create mode 100644 Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Tests.Console.Data.SqlServer.csproj create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer.sln create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Console.Data.SqlServer.csproj create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Data/Mappings/ExampleMapping.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Data/Models/Example.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContext.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContextFactory.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Dockerfile.Local create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260425181618_Initial.Designer.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260425181618_Initial.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Program.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Properties/InternalsVisibleTo.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/Workers/ExampleWorker.cs create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Development.json create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Production.json create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Staging.json create mode 100644 Console.Data.SqlServer/Console.Data.SqlServer/appsettings.json create mode 100644 Console.Data.SqlServer/Dockerfile create mode 100644 Console.Data.SqlServer/README.md create mode 100644 Console.Eventing.RabbitMq/.docker/docker-compose.dcproj create mode 100644 Console.Eventing.RabbitMq/.docker/docker-compose.yml create mode 100644 Console.Eventing.RabbitMq/.dockerignore create mode 100644 Console.Eventing.RabbitMq/.github/config/slack.yml create mode 100644 Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml create mode 100644 Console.Eventing.RabbitMq/.gitignore create mode 100644 Console.Eventing.RabbitMq/.kubernetes/configmap.yaml create mode 100644 Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml create mode 100644 Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Properties/DoNotParallelize.cs create mode 100644 Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Tests.Console.Eventing.RabbitMq.csproj create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.sln create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.csproj create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Dockerfile.Local create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/EventingHandler.cs create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/Models/EventModel.cs create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Program.cs create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Workers/ExampleWorker.cs create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Development.json create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Production.json create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Staging.json create mode 100644 Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.json create mode 100644 Console.Eventing.RabbitMq/Dockerfile create mode 100644 Console.Eventing.RabbitMq/README.md create mode 100644 Console.ExceptionHandling/.docker/docker-compose.dcproj create mode 100644 Console.ExceptionHandling/.docker/docker-compose.yml create mode 100644 Console.ExceptionHandling/.dockerignore create mode 100644 Console.ExceptionHandling/.github/config/slack.yml create mode 100644 Console.ExceptionHandling/.github/workflows/build-and-deploy.yml create mode 100644 Console.ExceptionHandling/.gitignore create mode 100644 Console.ExceptionHandling/.kubernetes/configmap.yaml create mode 100644 Console.ExceptionHandling/.kubernetes/cronjob.yaml create mode 100644 Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Properties/DoNotParallelize.cs create mode 100644 Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Tests.Console.ExceptionHandling.csproj create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling.sln create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Console.ExceptionHandling.csproj create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Dockerfile.Local create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Program.cs create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Properties/InternalsVisibleTo.cs create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/Workers/ExampleWorker.cs create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Development.json create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Production.json create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Staging.json create mode 100644 Console.ExceptionHandling/Console.ExceptionHandling/appsettings.json create mode 100644 Console.ExceptionHandling/Dockerfile create mode 100644 Console.ExceptionHandling/README.md create mode 100644 Console.Localization/.docker/docker-compose.dcproj create mode 100644 Console.Localization/.docker/docker-compose.yml create mode 100644 Console.Localization/.dockerignore create mode 100644 Console.Localization/.github/config/slack.yml create mode 100644 Console.Localization/.github/workflows/build-and-deploy.yml create mode 100644 Console.Localization/.gitignore create mode 100644 Console.Localization/.kubernetes/configmap.yaml create mode 100644 Console.Localization/.kubernetes/cronjob.yaml create mode 100644 Console.Localization/.tests/Tests.Console.Localization/Properties/DoNotParallelize.cs create mode 100644 Console.Localization/.tests/Tests.Console.Localization/Tests.Console.Localization.csproj create mode 100644 Console.Localization/Console.Localization.sln create mode 100644 Console.Localization/Console.Localization/Console.Localization.csproj create mode 100644 Console.Localization/Console.Localization/Dockerfile.Local create mode 100644 Console.Localization/Console.Localization/Program.cs create mode 100644 Console.Localization/Console.Localization/Properties/InternalsVisibleTo.cs create mode 100644 Console.Localization/Console.Localization/Workers/ExampleWorker.cs create mode 100644 Console.Localization/Console.Localization/appsettings.Development.json create mode 100644 Console.Localization/Console.Localization/appsettings.Production.json create mode 100644 Console.Localization/Console.Localization/appsettings.Staging.json create mode 100644 Console.Localization/Console.Localization/appsettings.json create mode 100644 Console.Localization/Dockerfile create mode 100644 Console.Localization/README.md create mode 100644 Console.Logging.Log4Net/.docker/docker-compose.dcproj create mode 100644 Console.Logging.Log4Net/.docker/docker-compose.yml create mode 100644 Console.Logging.Log4Net/.dockerignore create mode 100644 Console.Logging.Log4Net/.github/config/slack.yml create mode 100644 Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml create mode 100644 Console.Logging.Log4Net/.gitignore create mode 100644 Console.Logging.Log4Net/.kubernetes/configmap.yaml create mode 100644 Console.Logging.Log4Net/.kubernetes/cronjob.yaml create mode 100644 Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Properties/DoNotParallelize.cs create mode 100644 Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Tests.Console.Logging.Log4Net.csproj create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net.sln create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Console.Logging.Log4Net.csproj create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Dockerfile.Local create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Program.cs create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Properties/InternalsVisibleTo.cs create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/Workers/ExampleWorker.cs create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Development.json create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Production.json create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Staging.json create mode 100644 Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.json create mode 100644 Console.Logging.Log4Net/Dockerfile create mode 100644 Console.Logging.Log4Net/README.md create mode 100644 Console.Logging.Microsoft/.docker/docker-compose.dcproj create mode 100644 Console.Logging.Microsoft/.docker/docker-compose.yml create mode 100644 Console.Logging.Microsoft/.dockerignore create mode 100644 Console.Logging.Microsoft/.github/config/slack.yml create mode 100644 Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml create mode 100644 Console.Logging.Microsoft/.gitignore create mode 100644 Console.Logging.Microsoft/.kubernetes/configmap.yaml create mode 100644 Console.Logging.Microsoft/.kubernetes/cronjob.yaml create mode 100644 Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Properties/DoNotParallelize.cs create mode 100644 Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Tests.Console.Logging.Microsoft.csproj create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft.sln create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Console.Logging.Microsoft.csproj create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Dockerfile.Local create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Program.cs create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Properties/InternalsVisibleTo.cs create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/Workers/ExampleWorker.cs create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Development.json create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Production.json create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Staging.json create mode 100644 Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.json create mode 100644 Console.Logging.Microsoft/Dockerfile create mode 100644 Console.Logging.Microsoft/README.md create mode 100644 Console.Logging.NLog/.docker/docker-compose.dcproj create mode 100644 Console.Logging.NLog/.docker/docker-compose.yml create mode 100644 Console.Logging.NLog/.dockerignore create mode 100644 Console.Logging.NLog/.github/config/slack.yml create mode 100644 Console.Logging.NLog/.github/workflows/build-and-deploy.yml create mode 100644 Console.Logging.NLog/.gitignore create mode 100644 Console.Logging.NLog/.kubernetes/configmap.yaml create mode 100644 Console.Logging.NLog/.kubernetes/cronjob.yaml create mode 100644 Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Properties/DoNotParallelize.cs create mode 100644 Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Tests.Console.Logging.NLog.csproj create mode 100644 Console.Logging.NLog/Console.Logging.NLog.sln create mode 100644 Console.Logging.NLog/Console.Logging.NLog/Console.Logging.NLog.csproj create mode 100644 Console.Logging.NLog/Console.Logging.NLog/Dockerfile.Local create mode 100644 Console.Logging.NLog/Console.Logging.NLog/Program.cs create mode 100644 Console.Logging.NLog/Console.Logging.NLog/Properties/InternalsVisibleTo.cs create mode 100644 Console.Logging.NLog/Console.Logging.NLog/Workers/ExampleWorker.cs create mode 100644 Console.Logging.NLog/Console.Logging.NLog/appsettings.Development.json create mode 100644 Console.Logging.NLog/Console.Logging.NLog/appsettings.Production.json create mode 100644 Console.Logging.NLog/Console.Logging.NLog/appsettings.Staging.json create mode 100644 Console.Logging.NLog/Console.Logging.NLog/appsettings.json create mode 100644 Console.Logging.NLog/Dockerfile create mode 100644 Console.Logging.NLog/README.md create mode 100644 Console.Logging.Serilog/.docker/docker-compose.dcproj create mode 100644 Console.Logging.Serilog/.docker/docker-compose.yml create mode 100644 Console.Logging.Serilog/.dockerignore create mode 100644 Console.Logging.Serilog/.github/config/slack.yml create mode 100644 Console.Logging.Serilog/.github/workflows/build-and-deploy.yml create mode 100644 Console.Logging.Serilog/.gitignore create mode 100644 Console.Logging.Serilog/.kubernetes/configmap.yaml create mode 100644 Console.Logging.Serilog/.kubernetes/cronjob.yaml create mode 100644 Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Properties/DoNotParallelize.cs create mode 100644 Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Tests.Console.Logging.Serilog.csproj create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog.sln create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Console.Logging.Serilog.csproj create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Dockerfile.Local create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Program.cs create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Properties/InternalsVisibleTo.cs create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/Workers/ExampleWorker.cs create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Development.json create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Production.json create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Staging.json create mode 100644 Console.Logging.Serilog/Console.Logging.Serilog/appsettings.json create mode 100644 Console.Logging.Serilog/Dockerfile create mode 100644 Console.Logging.Serilog/README.md create mode 100644 Console.StartupTasks/.docker/docker-compose.dcproj create mode 100644 Console.StartupTasks/.docker/docker-compose.yml create mode 100644 Console.StartupTasks/.dockerignore create mode 100644 Console.StartupTasks/.github/config/slack.yml create mode 100644 Console.StartupTasks/.github/workflows/build-and-deploy.yml create mode 100644 Console.StartupTasks/.gitignore create mode 100644 Console.StartupTasks/.kubernetes/configmap.yaml create mode 100644 Console.StartupTasks/.kubernetes/cronjob.yaml create mode 100644 Console.StartupTasks/.tests/Tests.Console.StartupTasks/Properties/DoNotParallelize.cs create mode 100644 Console.StartupTasks/.tests/Tests.Console.StartupTasks/Tests.Console.StartupTasks.csproj create mode 100644 Console.StartupTasks/Console.StartupTasks.sln create mode 100644 Console.StartupTasks/Console.StartupTasks/Console.StartupTasks.csproj create mode 100644 Console.StartupTasks/Console.StartupTasks/Dockerfile.Local create mode 100644 Console.StartupTasks/Console.StartupTasks/Program.cs create mode 100644 Console.StartupTasks/Console.StartupTasks/Properties/InternalsVisibleTo.cs create mode 100644 Console.StartupTasks/Console.StartupTasks/Startup/ExampleStartupTask.cs create mode 100644 Console.StartupTasks/Console.StartupTasks/Workers/ExampleWorker.cs create mode 100644 Console.StartupTasks/Console.StartupTasks/appsettings.Development.json create mode 100644 Console.StartupTasks/Console.StartupTasks/appsettings.Production.json create mode 100644 Console.StartupTasks/Console.StartupTasks/appsettings.Staging.json create mode 100644 Console.StartupTasks/Console.StartupTasks/appsettings.json create mode 100644 Console.StartupTasks/Dockerfile create mode 100644 Console.StartupTasks/README.md create mode 100644 Console.Storage.Azure/.docker/docker-compose.dcproj create mode 100644 Console.Storage.Azure/.docker/docker-compose.yml create mode 100644 Console.Storage.Azure/.dockerignore create mode 100644 Console.Storage.Azure/.github/config/slack.yml create mode 100644 Console.Storage.Azure/.github/workflows/build-and-deploy.yml create mode 100644 Console.Storage.Azure/.gitignore create mode 100644 Console.Storage.Azure/.kubernetes/configmap.yaml create mode 100644 Console.Storage.Azure/.kubernetes/cronjob.yaml create mode 100644 Console.Storage.Azure/.kubernetes/storage-pv.yaml create mode 100644 Console.Storage.Azure/.kubernetes/storage-pvc.yaml create mode 100644 Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Properties/DoNotParallelize.cs create mode 100644 Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Tests.Console.Storage.Azure.csproj create mode 100644 Console.Storage.Azure/Console.Storage.Azure.sln create mode 100644 Console.Storage.Azure/Console.Storage.Azure/Console.Storage.Azure.csproj create mode 100644 Console.Storage.Azure/Console.Storage.Azure/Dockerfile.Local create mode 100644 Console.Storage.Azure/Console.Storage.Azure/Program.cs create mode 100644 Console.Storage.Azure/Console.Storage.Azure/Properties/InternalsVisibleTo.cs create mode 100644 Console.Storage.Azure/Console.Storage.Azure/Workers/ExampleWorker.cs create mode 100644 Console.Storage.Azure/Console.Storage.Azure/appsettings.Development.json create mode 100644 Console.Storage.Azure/Console.Storage.Azure/appsettings.Production.json create mode 100644 Console.Storage.Azure/Console.Storage.Azure/appsettings.Staging.json create mode 100644 Console.Storage.Azure/Console.Storage.Azure/appsettings.json create mode 100644 Console.Storage.Azure/Dockerfile create mode 100644 Console.Storage.Azure/README.md create mode 100644 Console.Storage.Local/.docker/docker-compose.dcproj create mode 100644 Console.Storage.Local/.docker/docker-compose.yml create mode 100644 Console.Storage.Local/.dockerignore create mode 100644 Console.Storage.Local/.github/config/slack.yml create mode 100644 Console.Storage.Local/.github/workflows/build-and-deploy.yml create mode 100644 Console.Storage.Local/.gitignore create mode 100644 Console.Storage.Local/.kubernetes/configmap.yaml create mode 100644 Console.Storage.Local/.kubernetes/cronjob.yaml create mode 100644 Console.Storage.Local/.kubernetes/storage-pvc.yaml create mode 100644 Console.Storage.Local/.kubernetes/storage-storageclass.yaml create mode 100644 Console.Storage.Local/.tests/Tests.Console.Storage.Local/Properties/DoNotParallelize.cs create mode 100644 Console.Storage.Local/.tests/Tests.Console.Storage.Local/Tests.Console.Storage.Local.csproj create mode 100644 Console.Storage.Local/Console.Storage.Local.sln create mode 100644 Console.Storage.Local/Console.Storage.Local/Console.Storage.Local.csproj create mode 100644 Console.Storage.Local/Console.Storage.Local/Dockerfile.Local create mode 100644 Console.Storage.Local/Console.Storage.Local/Program.cs create mode 100644 Console.Storage.Local/Console.Storage.Local/Properties/InternalsVisibleTo.cs create mode 100644 Console.Storage.Local/Console.Storage.Local/Workers/ExampleWorker.cs create mode 100644 Console.Storage.Local/Console.Storage.Local/appsettings.Development.json create mode 100644 Console.Storage.Local/Console.Storage.Local/appsettings.Production.json create mode 100644 Console.Storage.Local/Console.Storage.Local/appsettings.Staging.json create mode 100644 Console.Storage.Local/Console.Storage.Local/appsettings.json create mode 100644 Console.Storage.Local/Dockerfile create mode 100644 Console.Storage.Local/README.md create mode 100644 Console.Workers/.docker/docker-compose.dcproj create mode 100644 Console.Workers/.docker/docker-compose.yml create mode 100644 Console.Workers/.dockerignore create mode 100644 Console.Workers/.github/config/slack.yml create mode 100644 Console.Workers/.github/workflows/build-and-deploy.yml create mode 100644 Console.Workers/.gitignore create mode 100644 Console.Workers/.kubernetes/configmap.yaml create mode 100644 Console.Workers/.kubernetes/cronjob.yaml create mode 100644 Console.Workers/.tests/Tests.Console.Workers/Properties/DoNotParallelize.cs create mode 100644 Console.Workers/.tests/Tests.Console.Workers/Tests.Console.Workers.csproj create mode 100644 Console.Workers/Console.Workers.sln create mode 100644 Console.Workers/Console.Workers/Console.Workers.csproj create mode 100644 Console.Workers/Console.Workers/Dockerfile.Local create mode 100644 Console.Workers/Console.Workers/Program.cs create mode 100644 Console.Workers/Console.Workers/Properties/InternalsVisibleTo.cs create mode 100644 Console.Workers/Console.Workers/Workers/AnotherExampleWorker.cs create mode 100644 Console.Workers/Console.Workers/Workers/ExampleWorker.cs create mode 100644 Console.Workers/Console.Workers/appsettings.Development.json create mode 100644 Console.Workers/Console.Workers/appsettings.Production.json create mode 100644 Console.Workers/Console.Workers/appsettings.Staging.json create mode 100644 Console.Workers/Console.Workers/appsettings.json create mode 100644 Console.Workers/Dockerfile create mode 100644 Console.Workers/README.md create mode 100644 Console._Blank/.docker/docker-compose.dcproj create mode 100644 Console._Blank/.docker/docker-compose.yml create mode 100644 Console._Blank/.dockerignore create mode 100644 Console._Blank/.github/config/slack.yml create mode 100644 Console._Blank/.github/workflows/build-and-deploy.yml create mode 100644 Console._Blank/.gitignore create mode 100644 Console._Blank/.kubernetes/configmap.yaml create mode 100644 Console._Blank/.kubernetes/cronjob.yaml create mode 100644 Console._Blank/.tests/Tests.Console.Blank/Properties/DoNotParallelize.cs create mode 100644 Console._Blank/.tests/Tests.Console.Blank/Tests.Console.Blank.csproj create mode 100644 Console._Blank/Console.Blank.sln create mode 100644 Console._Blank/Console.Blank/Console.Blank.csproj create mode 100644 Console._Blank/Console.Blank/Dockerfile.Local create mode 100644 Console._Blank/Console.Blank/Program.cs create mode 100644 Console._Blank/Console.Blank/Properties/InternalsVisibleTo.cs create mode 100644 Console._Blank/Console.Blank/appsettings.Development.json create mode 100644 Console._Blank/Console.Blank/appsettings.Production.json create mode 100644 Console._Blank/Console.Blank/appsettings.Staging.json create mode 100644 Console._Blank/Console.Blank/appsettings.json create mode 100644 Console._Blank/Dockerfile create mode 100644 Console._Blank/README.md create mode 100644 Nano.Lessons.sln.cmd create mode 100644 Web._Blank/.docker/docker-compose.dcproj create mode 100644 Web._Blank/.docker/docker-compose.yml create mode 100644 Web._Blank/.dockerignore create mode 100644 Web._Blank/.github/config/slack.yml create mode 100644 Web._Blank/.github/workflows/build-and-deploy.yml create mode 100644 Web._Blank/.gitignore create mode 100644 Web._Blank/.kubernetes/autoscaler.yaml create mode 100644 Web._Blank/.kubernetes/configmap.yaml create mode 100644 Web._Blank/.kubernetes/deployment.yaml create mode 100644 Web._Blank/.kubernetes/service.yaml create mode 100644 Web._Blank/.tests/Tests.Web.Blank/Properties/DoNotParallelize.cs create mode 100644 Web._Blank/.tests/Tests.Web.Blank/Tests.Web.Blank.csproj create mode 100644 Web._Blank/Dockerfile create mode 100644 Web._Blank/LICENSE create mode 100644 Web._Blank/README.md create mode 100644 Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj create mode 100644 Web._Blank/Web.Blank.sln create mode 100644 Web._Blank/Web.Blank/App.razor create mode 100644 Web._Blank/Web.Blank/Dockerfile.Local create mode 100644 Web._Blank/Web.Blank/Program.cs create mode 100644 Web._Blank/Web.Blank/Properties/InternalsVisibleTo.cs create mode 100644 Web._Blank/Web.Blank/Web.Blank.csproj create mode 100644 Web._Blank/Web.Blank/_Imports.razor create mode 100644 Web._Blank/Web.Blank/appsettings.Development.json create mode 100644 Web._Blank/Web.Blank/appsettings.Production.json create mode 100644 Web._Blank/Web.Blank/appsettings.Staging.json create mode 100644 Web._Blank/Web.Blank/appsettings.json create mode 100644 Web._Blank/icon.png diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..cfa292cd --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github:, [Nano-Core] diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..130b0217 --- /dev/null +++ b/.github/workflows/build-and-deploy.yml @@ -0,0 +1,34 @@ +name: Build and Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + VERSION: 10.0.0-rc1 +jobs: + build-and-deploy: + runs-on: ubuntu-latest + permissions: + contents: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: false + steps: + - uses: actions/checkout@v6 + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }}.${{ github.run_number }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: ${{ contains(env.VERSION, '-') }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ApiClients.Audit/.docker/docker-compose.dcproj b/Api.ApiClients.Audit/.docker/docker-compose.dcproj new file mode 100644 index 00000000..d9e8e500 --- /dev/null +++ b/Api.ApiClients.Audit/.docker/docker-compose.dcproj @@ -0,0 +1,13 @@ + + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Audit/.docker/docker-compose.yml b/Api.ApiClients.Audit/.docker/docker-compose.yml new file mode 100644 index 00000000..3701e77a --- /dev/null +++ b/Api.ApiClients.Audit/.docker/docker-compose.yml @@ -0,0 +1,44 @@ +services: + api.apiclients.audit: + image: api.apiclients.audit + hostname: api-apiclients-audit + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.ApiClients.Audit + dockerfile: "Dockerfile.Local" + networks: + - network + + api.apiclients.audit.service: + image: api.apiclients.audit.service + hostname: api-apiclients-audit-service + restart: on-failure + ports: + - 8181:8181 + build: + context: ../Api.ApiClients.Audit.Service + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.ApiClients.Audit/.dockerignore b/Api.ApiClients.Audit/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.ApiClients.Audit/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.ApiClients.Audit/.github/config/slack.yml b/Api.ApiClients.Audit/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.ApiClients.Audit/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml b/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..2e9dbcea --- /dev/null +++ b/Api.ApiClients.Audit/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.ApiClients.Audit + IMAGE_NAME: api.apiclients.audit + SERVICE_NAME: api-apiclients-audit + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ApiClients.Audit/.gitignore b/Api.ApiClients.Audit/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.ApiClients.Audit/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ApiClients.Audit/.kubernetes/autoscaler.yaml b/Api.ApiClients.Audit/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.ApiClients.Audit/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ApiClients.Audit/.kubernetes/configmap.yaml b/Api.ApiClients.Audit/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.ApiClients.Audit/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ApiClients.Audit/.kubernetes/deployment.yaml b/Api.ApiClients.Audit/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.ApiClients.Audit/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.ApiClients.Audit/.kubernetes/service.yaml b/Api.ApiClients.Audit/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.ApiClients.Audit/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Properties/DoNotParallelize.cs b/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Tests.Api.ApiClients.Audit.csproj b/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Tests.Api.ApiClients.Audit.csproj new file mode 100644 index 00000000..2e895288 --- /dev/null +++ b/Api.ApiClients.Audit/.tests/Tests.Api.ApiClients.Audit/Tests.Api.ApiClients.Audit.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Models/Api.ApiClients.Audit.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Api.ApiClients.Audit.Service.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ApiClient/NanoApiClient.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ApiClient/NanoApiClient.cs new file mode 100644 index 00000000..23e08224 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ApiClient/NanoApiClient.cs @@ -0,0 +1,8 @@ +using Nano.App.ApiClient; + +namespace Api.ApiClients.Audit.Service.Models.ApiClient; + +/// +/// Nano Api Client. +/// +public class NanoApiClient(Nano.App.ApiClient.ApiClient apiClient) : BaseApiClient(apiClient); \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Criterias/ExampleQueryCriteria.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..f35de66c --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.ApiClients.Audit.Service.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Example.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Example.cs new file mode 100644 index 00000000..0ed5bd69 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/Example.cs @@ -0,0 +1,28 @@ +using System; +using Nano.Data.Abstractions.Annotations; +using Nano.Data.Abstractions.Models; +using Nano.Data.Abstractions.Models.Abstractions; + +namespace Api.ApiClients.Audit.Service.Models; + +/// +/// Example. +/// +public class Example : BaseEntity, IEntityAuditable, IEntitySoftDeletable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; + + /// + /// Navigation Id. + /// + public virtual Guid? NavigationId { get; set; } + + /// + /// Navigation. + /// + [Include] + public virtual ExampleNavigation? Navigation { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ExampleNavigation.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ExampleNavigation.cs new file mode 100644 index 00000000..80fd08ef --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service.Models/ExampleNavigation.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Nano.Data.Abstractions.Models; +using Nano.Data.Abstractions.Models.Abstractions; + +namespace Api.ApiClients.Audit.Service.Models; + +/// +/// Example Navigation. +/// +public class ExampleNavigation : BaseEntity, IEntityAuditable, IEntitySoftDeletable +{ + /// + /// Navigation Name. + /// + public virtual string NavigationName { get; set; } = null!; + + /// + /// Examples. + /// + public virtual ICollection Examples { get; set; } = []; +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Api.ApiClients.Audit.Service.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Api.ApiClients.Audit.Service.csproj new file mode 100644 index 00000000..ffe64f93 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Api.ApiClients.Audit.Service.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/AuditController.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/AuditController.cs new file mode 100644 index 00000000..bd90f558 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/AuditController.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.ApiClients.Audit.Service.Controllers; + +/// +public class AuditController(ILogger logger, IRepository repository) + : BaseAuditController(logger, repository); \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/ExamplesController.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/ExamplesController.cs new file mode 100644 index 00000000..e5e8c595 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.ApiClients.Audit.Service.Models; +using Api.ApiClients.Audit.Service.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.ApiClients.Audit.Service.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleMapping.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..8b61ff28 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,34 @@ +using System; +using Api.ApiClients.Audit.Service.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using Nano.Data.Mappings.Extensions; + +namespace Api.ApiClients.Audit.Service.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasOne(x => x.Navigation) + .WithMany(x => x.Examples); + + builder + .OnUpdating(x => + { + x.Entity.Name += "-triggered"; + }); + } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleNavigationMapping.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleNavigationMapping.cs new file mode 100644 index 00000000..f9269c03 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/Mappings/ExampleNavigationMapping.cs @@ -0,0 +1,27 @@ +using System; +using Api.ApiClients.Audit.Service.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.ApiClients.Audit.Service.Data.Mappings; + +/// +/// Example Navigation Mapping. +/// +public class ExampleNavigationMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.NavigationName); + + builder + .HasMany(x => x.Examples) + .WithOne(x => x.Navigation); + } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContext.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContext.cs new file mode 100644 index 00000000..3dfa9c4a --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.ApiClients.Audit.Service.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContextFactory.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..4850809c --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.ApiClients.Audit.Service.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local new file mode 100644 index 00000000..1e6d12c6 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ApiClients.Audit.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.Designer.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.Designer.cs new file mode 100644 index 00000000..077c87f2 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.Designer.cs @@ -0,0 +1,728 @@ +// +using System; +using Api.ApiClients.Audit.Service.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Audit.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415133755_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Audit.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NavigationId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("NavigationId"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("NavigationName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNavigation"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNoAudit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNoAudit"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.Example", b => + { + b.HasOne("Api.Data.Audit.Models.ExampleNavigation", "Navigation") + .WithMany("Examples") + .HasForeignKey("NavigationId"); + + b.Navigation("Navigation"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => + { + b.Navigation("Examples"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.cs new file mode 100644 index 00000000..dade123c --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/20260415133755_Initial.cs @@ -0,0 +1,667 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Audit.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleNavigation", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NavigationName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleNavigation", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleNoAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleNoAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + NavigationId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + table.ForeignKey( + name: "FK_Example_ExampleNavigation_NavigationId", + column: x => x.NavigationId, + principalTable: "ExampleNavigation", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_NavigationId", + table: "Example", + column: "NavigationId"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNavigation_CreatedAt", + table: "ExampleNavigation", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNavigation_IsDeleted", + table: "ExampleNavigation", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNoAudit_CreatedAt", + table: "ExampleNoAudit", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNoAudit_IsDeleted", + table: "ExampleNoAudit", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "ExampleNoAudit"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "ExampleNavigation"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/MySqlDbContextModelSnapshot.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..ee5255c2 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,725 @@ +// +using System; +using Api.ApiClients.Audit.Service.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Audit.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Audit.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NavigationId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("NavigationId"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("NavigationName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNavigation"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNoAudit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNoAudit"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.Example", b => + { + b.HasOne("Api.Data.Audit.Models.ExampleNavigation", "Navigation") + .WithMany("Examples") + .HasForeignKey("NavigationId"); + + b.Navigation("Navigation"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => + { + b.Navigation("Examples"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Program.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Program.cs new file mode 100644 index 00000000..cd141ffd --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Program.cs @@ -0,0 +1,13 @@ +using Api.ApiClients.Audit.Service.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Properties/InternalsVisibleTo.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..bc18519e --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ApiClients.Audit")] \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Development.json b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Production.json b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Staging.json b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.json b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.json new file mode 100644 index 00000000..dbd68122 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.Service/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8181 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit.sln b/Api.ApiClients.Audit/Api.ApiClients.Audit.sln new file mode 100644 index 00000000..62da1d0c --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit.sln @@ -0,0 +1,159 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Audit.Models", "Api.ApiClients.Audit.Models\Api.ApiClients.Audit.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Audit", "Api.ApiClients.Audit\Api.ApiClients.Audit.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ApiClients.Audit", ".tests\Tests.Api.ApiClients.Audit\Tests.Api.ApiClients.Audit.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Audit.Service.Models", "Api.ApiClients.Audit.Service.Models\Api.ApiClients.Audit.Service.Models.csproj", "{72C75C0B-EACD-A113-B9A4-2600519A71E0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Audit.Service", "Api.ApiClients.Audit.Service\Api.ApiClients.Audit.Service.csproj", "{37CEC825-166A-5BB7-9466-A75FFE32383F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.Build.0 = Release|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/Api.ApiClients.Audit.csproj b/Api.ApiClients.Audit/Api.ApiClients.Audit/Api.ApiClients.Audit.csproj new file mode 100644 index 00000000..73fac6b6 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/Api.ApiClients.Audit.csproj @@ -0,0 +1,38 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/Controllers/AuditController.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit/Controllers/AuditController.cs new file mode 100644 index 00000000..b651fe57 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/Controllers/AuditController.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.ApiClients.Audit.Service.Models.ApiClient; +using Api.ApiClients.Audit.Service.Models.Criterias; +using DynamicExpression.Interfaces; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.ApiClient.Requests; +using Nano.Common.Consts; +using Nano.Data.Abstractions.Models; + +namespace Api.ApiClients.Audit.Controllers; + +/// +/// Controller with audit. +/// +/// The . +/// The . +public class AuditController(ILogger logger, NanoApiClient nanoApiClient) + : BaseController(logger) +{ + private readonly NanoApiClient nanoApiClient = nanoApiClient ?? throw new ArgumentNullException(nameof(nanoApiClient)); + + /// + /// Gets all entities matching the specified query. + /// + /// The query used to filter entities. + /// Optional include depth for related entities. + /// The cancellation token. + /// A collection of entities matching the query. + /// Entities retrieved successfully. + /// Invalid query parameters. + /// Unauthorized access. + /// No entities found. + /// Internal server error. + [HttpGet] + [Route(ActionRoutes.INDEX)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task IndexAsync([FromQuery][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var examples = await this.nanoApiClient.Audit + .IndexAsync(new IndexRequest + { + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(examples); + } + + /// + /// Gets a single entity by its identifier. + /// + /// The identifier of the entity. + /// Optional include depth for related entities. + /// The cancellation token. + /// The entity matching the identifier. + /// Entity retrieved successfully. + /// Invalid identifier. + /// Unauthorized access. + /// Entity not found. + /// Internal server error. + [HttpGet] + [Route(ActionRoutes.DETAILS)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(AuditEntry), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DetailsAsync([FromRoute][Required] Guid id, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var examples = await this.nanoApiClient.Audit + .DetailsAsync(new DetailsRequest + { + Id = id, + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(examples); + } + + /// + /// Gets multiple entities by their identifiers. + /// + /// The identifiers of the entities. + /// Optional include depth for related entities. + /// The cancellation token. + /// The entities matching the identifiers. + /// Entities retrieved successfully. + /// Invalid identifiers. + /// Unauthorized access. + /// No entities found. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.DETAILS_MANY)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DetailsManyPostAsync([FromBody][Required] Guid[] ids, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var examples = await this.nanoApiClient.Audit + .DetailsManyAsync(new DetailsManyRequest + { + Ids = ids, + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(examples); + } + + /// + /// Queries entities matching the specified criteria. + /// + /// The query model containing filters and criteria. + /// Optional include depth for related entities. + /// The cancellation token. + /// A collection of entities matching the criteria. + /// Entities retrieved successfully. + /// Invalid query parameters. + /// Unauthorized access. + /// No entities found. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.QUERY)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task QueryPostAsync([FromBody][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var examples = await this.nanoApiClient.Audit + .QueryAsync(new QueryRequest + { + Query = query, + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(examples); + } + + /// + /// Retrieves the first entity matching the specified criteria. + /// + /// The query model containing filters and criteria. + /// Optional include depth for related entities. + /// The cancellation token. + /// The first entity matching the criteria. + /// Entity retrieved successfully. + /// Invalid query parameters. + /// Unauthorized access. + /// No entity found. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.QUERY_FIRST)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(AuditEntry), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task QueryFirstPostAsync([FromBody][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Audit + .QueryFirstAsync(new QueryFirstRequest + { + Query = query, + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(example); + } + + /// + /// Gets the total count of entities matching the specified criteria. + /// + /// The criteria model containing filters. + /// The cancellation token. + /// The number of entities matching the criteria. + /// Count retrieved successfully. + /// Invalid criteria parameters. + /// Unauthorized access. + /// No entities found. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.QUERY_COUNT)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task QueryCountPostAsync([FromBody][Required] ExampleQueryCriteria criteria, CancellationToken cancellationToken = default) + { + var count = await this.nanoApiClient.Audit + .QueryCountAsync(new QueryCountRequest + { + Criteria = criteria + }, cancellationToken); + + return this.Ok(count); + } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/Controllers/ExamplesController.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit/Controllers/ExamplesController.cs new file mode 100644 index 00000000..2b0ad6e8 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/Controllers/ExamplesController.cs @@ -0,0 +1,53 @@ +using Api.ApiClients.Audit.Service.Models.ApiClient; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.ApiClient.Requests; +using Nano.Common.Consts; +using System; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.ApiClients.Audit.Service.Models; + +namespace Api.ApiClients.Audit.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, NanoApiClient nanoApiClient) : BaseController(logger) +{ + private readonly NanoApiClient nanoApiClient = nanoApiClient ?? throw new ArgumentNullException(nameof(nanoApiClient)); + + /// + /// Creates a single model instance. + /// + /// The entity to create. + /// Cancellation token. + /// The created entity. + /// Entity created. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.CREATE)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.Created)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task CreateAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Entity + .CreateAsync(new CreateRequest + { + Entity = entity + }, cancellationToken); + + return this.Created(ActionRoutes.CREATE, example); + } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/Dockerfile.Local b/Api.ApiClients.Audit/Api.ApiClients.Audit/Dockerfile.Local new file mode 100644 index 00000000..f55f6733 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ApiClients.Audit.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/Program.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/Properties/InternalsVisibleTo.cs b/Api.ApiClients.Audit/Api.ApiClients.Audit/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..bc18519e --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ApiClients.Audit")] \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Development.json b/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Production.json b/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Staging.json b/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.json b/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.json new file mode 100644 index 00000000..49779fd9 --- /dev/null +++ b/Api.ApiClients.Audit/Api.ApiClients.Audit/appsettings.json @@ -0,0 +1,26 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { }, + "Apis": { + "NanoApiClient": { + "Host": "api.apiclients.audit.service", + "Root": "api", + "Port": 8181, + "UseSsl": false, + "Timeout": "00:00:30", + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } + } + } +} \ No newline at end of file diff --git a/Api.ApiClients.Audit/Dockerfile b/Api.ApiClients.Audit/Dockerfile new file mode 100644 index 00000000..ae5aa0b8 --- /dev/null +++ b/Api.ApiClients.Audit/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.ApiClients.Audit.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Audit/LICENSE b/Api.ApiClients.Audit/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.ApiClients.Audit/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.ApiClients.Audit/README.md b/Api.ApiClients.Audit/README.md new file mode 100644 index 00000000..4b634acf --- /dev/null +++ b/Api.ApiClients.Audit/README.md @@ -0,0 +1,42 @@ +# Api.ApiClients.Audit + +> _Nano API application with api-client audit._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.ApiClients](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.ApiClients)**. All the custom methods have been removed and replaced +with corresponding methods for each audit controller operation in the inner service. + +The inner application has a data provider enabled to demonstrate the generic API client integration with Nano entity models. A `NanoApiClient` implementation, derived from +`BaseApiClient`, has been added to the service application. This lesson showcases how to use the `Audit` sub-group of the `BaseApiClient` to interact with any audit data +exposed by the inner application. + +The endpoints mirror those of the `AuditController` in the inner service, allowing each entity action to be invoked through the API client for demonstration purposes. In a +real-world scenario, this structure would typically differ. The outer application would define its own request and response contracts tailored to its domain. However, for +simplicity and clarity in this example, the responses from the inner service are passed directly through the outer API. + +The following endpoint is available for testing. + +| Endpoint | Description | +| -------------------------------------------- | ----------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/create` | Returns a `200 OK` response. Creates an `Example` with nested `ExampleNavigation` which is audited. | + +> 📖 Learn more about **[Nano Api Clients](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App/README.md#api-clients)**. + + + + + + diff --git a/Api.ApiClients.Audit/icon.png b/Api.ApiClients.Audit/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Entity/.docker/docker-compose.yml b/Api.ApiClients.Entity/.docker/docker-compose.yml new file mode 100644 index 00000000..c93a17e7 --- /dev/null +++ b/Api.ApiClients.Entity/.docker/docker-compose.yml @@ -0,0 +1,44 @@ +services: + api.apiclients.entity: + image: api.apiclients.entity + hostname: api.apiclients.entity + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.ApiClients.Entity + dockerfile: "Dockerfile.Local" + networks: + - network + + api.apiclients.entity.service: + image: api.apiclients.entity.service + hostname: api-apiclients-entity-service + restart: on-failure + ports: + - 8181:8181 + build: + context: ../Api.ApiClients.Entity.Service + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.ApiClients.Entity/.dockerignore b/Api.ApiClients.Entity/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.ApiClients.Entity/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.ApiClients.Entity/.github/config/slack.yml b/Api.ApiClients.Entity/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.ApiClients.Entity/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml b/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..bec796fc --- /dev/null +++ b/Api.ApiClients.Entity/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.ApiClients.Entity + IMAGE_NAME: api.apiclients.entity + SERVICE_NAME: api-apiclients-entity + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ApiClients.Entity/.gitignore b/Api.ApiClients.Entity/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.ApiClients.Entity/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ApiClients.Entity/.kubernetes/autoscaler.yaml b/Api.ApiClients.Entity/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.ApiClients.Entity/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ApiClients.Entity/.kubernetes/configmap.yaml b/Api.ApiClients.Entity/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.ApiClients.Entity/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ApiClients.Entity/.kubernetes/deployment.yaml b/Api.ApiClients.Entity/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.ApiClients.Entity/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.ApiClients.Entity/.kubernetes/service.yaml b/Api.ApiClients.Entity/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.ApiClients.Entity/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Properties/DoNotParallelize.cs b/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Tests.Api.ApiClients.Entity.csproj b/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Tests.Api.ApiClients.Entity.csproj new file mode 100644 index 00000000..1ef66e7c --- /dev/null +++ b/Api.ApiClients.Entity/.tests/Tests.Api.ApiClients.Entity/Tests.Api.ApiClients.Entity.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Models/Api.ApiClients.Entity.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api.ApiClients.Entity.Service.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api/NanoApiClient.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api/NanoApiClient.cs new file mode 100644 index 00000000..ab08eb1d --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Api/NanoApiClient.cs @@ -0,0 +1,8 @@ +using Nano.App.ApiClient; + +namespace Api.ApiClients.Entity.Service.Models.Api; + +/// +/// Nano Api Client. +/// +public class NanoApiClient(Nano.App.ApiClient.ApiClient apiClient) : BaseApiClient(apiClient); \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Criterias/ExampleQueryCriteria.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..93981b5c --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.ApiClients.Entity.Service.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Example.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Example.cs new file mode 100644 index 00000000..82066324 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.ApiClients.Entity.Service.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Api.ApiClients.Entity.Service.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Api.ApiClients.Entity.Service.csproj new file mode 100644 index 00000000..db9ec4c1 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Api.ApiClients.Entity.Service.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Controllers/ExamplesController.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Controllers/ExamplesController.cs new file mode 100644 index 00000000..8ccd2e54 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.ApiClients.Entity.Service.Models; +using Api.ApiClients.Entity.Service.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.ApiClients.Entity.Service.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/Mappings/ExampleMapping.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..6a2ed7b4 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.ApiClients.Entity.Service.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.ApiClients.Entity.Service.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContext.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContext.cs new file mode 100644 index 00000000..add58857 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.ApiClients.Entity.Service.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContextFactory.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..f811c214 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.ApiClients.Entity.Service.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local new file mode 100644 index 00000000..9223f500 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ApiClients.Entity.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.Designer.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.Designer.cs new file mode 100644 index 00000000..4859280c --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.Designer.cs @@ -0,0 +1,651 @@ +// +using System; +using Api.ApiClients.Entity.Service.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.ApiClient.Service.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260422105215_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.ApiClient.Service.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.cs new file mode 100644 index 00000000..aa2ac68e --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/20260422105215_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.ApiClient.Service.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/MySqlDbContextModelSnapshot.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..c1e0cae7 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,648 @@ +// +using System; +using Api.ApiClients.Entity.Service.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.ApiClient.Service.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.ApiClient.Service.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Program.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Program.cs new file mode 100644 index 00000000..78918468 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Program.cs @@ -0,0 +1,13 @@ +using Api.ApiClients.Entity.Service.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Properties/InternalsVisibleTo.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..9a663cce --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ApiClients.Entity")] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Development.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Production.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Staging.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json new file mode 100644 index 00000000..dbd68122 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.Service/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8181 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity.sln b/Api.ApiClients.Entity/Api.ApiClients.Entity.sln new file mode 100644 index 00000000..9d759bd7 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity.sln @@ -0,0 +1,159 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Entity.Models", "Api.ApiClients.Entity.Models\Api.ApiClients.Entity.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Entity", "Api.ApiClients.Entity\Api.ApiClients.Entity.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ApiClients.Entity", ".tests\Tests.Api.ApiClients.Entity\Tests.Api.ApiClients.Entity.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Entity.Service.Models", "Api.ApiClients.Entity.Service.Models\Api.ApiClients.Entity.Service.Models.csproj", "{72C75C0B-EACD-A113-B9A4-2600519A71E0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Entity.Service", "Api.ApiClients.Entity.Service\Api.ApiClients.Entity.Service.csproj", "{37CEC825-166A-5BB7-9466-A75FFE32383F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.Build.0 = Release|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Api.ApiClients.Entity.csproj b/Api.ApiClients.Entity/Api.ApiClients.Entity/Api.ApiClients.Entity.csproj new file mode 100644 index 00000000..71b6269b --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/Api.ApiClients.Entity.csproj @@ -0,0 +1,38 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Controllers/ExamplesController.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity/Controllers/ExamplesController.cs new file mode 100644 index 00000000..3e211752 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/Controllers/ExamplesController.cs @@ -0,0 +1,732 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.ApiClients.Entity.Service.Models; +using Api.ApiClients.Entity.Service.Models.Api; +using Api.ApiClients.Entity.Service.Models.Criterias; +using DynamicExpression.Interfaces; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.ApiClient.Requests; +using Nano.App.ApiClient.Requests.Models; +using Nano.Common.Consts; + +namespace Api.ApiClients.Entity.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, NanoApiClient nanoApiClient) : BaseController(logger) +{ + private readonly NanoApiClient nanoApiClient = nanoApiClient ?? throw new ArgumentNullException(nameof(nanoApiClient)); + + + #region Read + + /// + /// Gets all entities matching the specified query. + /// + /// The query used to filter entities. + /// Optional include depth for related entities. + /// The cancellation token. + /// A collection of entities matching the query. + /// Entities retrieved successfully. + /// Invalid query parameters. + /// Unauthorized access. + /// No entities found. + /// Internal server error. + [HttpGet] + [Route(ActionRoutes.INDEX)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task IndexAsync([FromQuery][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var examples = await this.nanoApiClient.Entity + .IndexAsync(new IndexRequest + { + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(examples); + } + + /// + /// Gets a single entity by its identifier. + /// + /// The identifier of the entity. + /// Optional include depth for related entities. + /// The cancellation token. + /// The entity matching the identifier. + /// Entity retrieved successfully. + /// Invalid identifier. + /// Unauthorized access. + /// Entity not found. + /// Internal server error. + [HttpGet] + [Route(ActionRoutes.DETAILS)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DetailsAsync([FromRoute][Required] Guid id, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var examples = await this.nanoApiClient.Entity + .DetailsAsync(new DetailsRequest + { + Id = id, + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(examples); + } + + /// + /// Gets multiple entities by their identifiers. + /// + /// The identifiers of the entities. + /// Optional include depth for related entities. + /// The cancellation token. + /// The entities matching the identifiers. + /// Entities retrieved successfully. + /// Invalid identifiers. + /// Unauthorized access. + /// No entities found. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.DETAILS_MANY)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DetailsManyPostAsync([FromBody][Required] Guid[] ids, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var examples = await this.nanoApiClient.Entity + .DetailsManyAsync(new DetailsManyRequest + { + Ids = ids, + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(examples); + } + + /// + /// Queries entities matching the specified criteria. + /// + /// The query model containing filters and criteria. + /// Optional include depth for related entities. + /// The cancellation token. + /// A collection of entities matching the criteria. + /// Entities retrieved successfully. + /// Invalid query parameters. + /// Unauthorized access. + /// No entities found. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.QUERY)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task QueryPostAsync([FromBody][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var examples = await this.nanoApiClient.Entity + .QueryAsync(new QueryRequest + { + Query = query, + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(examples); + } + + /// + /// Retrieves the first entity matching the specified criteria. + /// + /// The query model containing filters and criteria. + /// Optional include depth for related entities. + /// The cancellation token. + /// The first entity matching the criteria. + /// Entity retrieved successfully. + /// Invalid query parameters. + /// Unauthorized access. + /// No entity found. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.QUERY_FIRST)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task QueryFirstPostAsync([FromBody][Required] IQuery query, [FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Entity + .QueryFirstAsync(new QueryFirstRequest + { + Query = query, + IncludeDepth = includeDepth + }, cancellationToken); + + return this.Ok(example); + } + + /// + /// Gets the total count of entities matching the specified criteria. + /// + /// The criteria model containing filters. + /// The cancellation token. + /// The number of entities matching the criteria. + /// Count retrieved successfully. + /// Invalid criteria parameters. + /// Unauthorized access. + /// No entities found. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.QUERY_COUNT)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task QueryCountPostAsync([FromBody][Required] ExampleQueryCriteria criteria, CancellationToken cancellationToken = default) + { + var count = await this.nanoApiClient.Entity + .QueryCountAsync(new QueryCountRequest + { + Criteria = criteria + }, cancellationToken); + + return this.Ok(count); + } + + #endregion + + + #region Create + + /// + /// Creates a single model instance. + /// + /// The entity to create. + /// Cancellation token. + /// The created entity. + /// Entity created. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.CREATE)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.Created)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task CreateAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Entity + .CreateAsync(new CreateRequest + { + Entity = entity + }, cancellationToken); + + return this.Created(ActionRoutes.CREATE, example); + } + + /// + /// Creates or edits a single model instance. + /// + /// The entity to create. + /// Cancellation token. + /// The created or edited entity. + /// Entity created. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.CREATE_OR_EDIT)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task CreateOrEditAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Entity + .CreateOrEditAsync(new CreateOrEditRequest + { + Entity = entity + }, cancellationToken); + + return this.Ok(example); + } + + /// + /// Creates a single model instance or if it already exist, retrieves it with included navigations. + /// + /// The entity to create. + /// Cancellation token. + /// The created entity with included navigations. + /// Entity created. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.CREATE_OR_GET)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.Created)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task CreateOrGetAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Entity + .CreateOrGetAsync(new CreateOrGetRequest + { + Entity = entity + }, cancellationToken); + + return this.Created(ActionRoutes.CREATE_OR_GET, example); + } + + /// + /// Creates a single model instance and retrieves it with included navigations. + /// + /// The entity to create. + /// Cancellation token. + /// The created entity with included navigations. + /// Entity created. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.CREATE_AND_GET)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.Created)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task CreateAndGetAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Entity + .CreateAndGetAsync(new CreateAndGetRequest + { + Entity = entity + }, cancellationToken); + + return this.Created(ActionRoutes.CREATE_AND_GET, example); + } + + /// + /// Creates multiple model instances. + /// + /// The entities to create. + /// Cancellation token. + /// Void. + /// Entities created. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.CREATE_MANY)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.Created)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task CreateManyAsync([FromBody][Required] IEnumerable entities, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .CreateManyAsync(new CreateManyRequest + { + Entities = entities + }, cancellationToken); + + return this.Created(); + } + + /// + /// Creates multiple model instances in bulk. + /// + /// The entities to create. + /// Cancellation token. + /// Void. + /// Ok. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPost] + [Route(ActionRoutes.CREATE_MANY_BULK)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.Created)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task CreateManyBulkAsync([FromBody][Required] IEnumerable entities, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .CreateManyBulkAsync(new CreateManyBulkRequest + { + Entities = entities + }, cancellationToken); + + return this.Created(); + } + + #endregion + + + #region Edit + + /// + /// Edits a single model instance. + /// + /// The entity to edit. + /// Cancellation token. + /// The edited entity. + /// Entity updated. + /// Entity not found. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPut] + [Route(ActionRoutes.EDIT)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task EditAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Entity + .EditAsync(new EditRequest + { + Entity = entity + }, cancellationToken); + + return this.Ok(example); + } + + /// + /// Edits a single model instance and retrieves it with included navigations. + /// + /// The entity to edit. + /// Cancellation token. + /// The edited entity. + /// Entity updated. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPut] + [Route(ActionRoutes.EDIT_GET)] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(Example), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task EditAndGetAsync([FromBody][Required] Example entity, CancellationToken cancellationToken = default) + { + var example = await this.nanoApiClient.Entity + .EditAndGetAsync(new EditAndGetRequest + { + Entity = entity + }, cancellationToken); + + return this.Ok(example); + } + + /// + /// Edits multiple model instances. + /// + /// The entities to edit. + /// Cancellation token. + /// Void. + /// Entities updated. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPut] + [Route(ActionRoutes.EDIT_MANY)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task EditManyAsync([FromBody][Required] IEnumerable entities, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .EditManyAsync(new EditManyRequest + { + Entities = entities + }, cancellationToken); + + return this.Ok(); + } + + /// + /// Edits multiple model instances in bulk. + /// + /// The entities to edit. + /// Cancellation token. + /// Void. + /// Entities updated. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPut] + [Route(ActionRoutes.EDIT_MANY_BULK)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task EditManyBulkAsync([FromBody][Required] IEnumerable entities, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .EditManyBulkAsync(new EditManyBulkRequest + { + Entities = entities + }, cancellationToken); + + return this.Ok(); + } + + /// + /// Edits entities that match the specified criteria. + /// + /// The update query containing criteria and property updates. + /// Cancellation token. + /// Void. + /// Entities updated. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPut] + [Route(ActionRoutes.EDIT_QUERY)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task EditQueryAsync([FromBody][Required] UpdateQuery query, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .EditQueryAsync(new EditQueryRequest + { + Query = query + }, cancellationToken); + + return this.Ok(); + } + + /// + /// Edits entities that match the specified criteria in bulk. + /// + /// The update query containing criteria and property updates. + /// Cancellation token. + /// Void. + /// Entities updated. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpPut] + [Route(ActionRoutes.EDIT_QUERY_BULK)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task EditQueryBulkAsync([FromBody][Required] UpdateQuery query, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .EditQueryBulkAsync(new EditQueryBulkRequest + { + Query = query + }, cancellationToken); + + return this.Ok(); + } + + #endregion + + + #region Delete + + /// + /// Deletes a single entity by its identifier. + /// + /// The identifier of the entity to delete. + /// Cancellation token. + /// Void. + /// Entity deleted. + /// Bad request. + /// Unauthorized. + /// Entity not found. + /// Internal server error. + [HttpDelete] + [Route(ActionRoutes.DELETE)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DeleteAsync([FromRoute][Required] Guid id, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .DeleteAsync(new DeleteRequest + { + Id = id + }, cancellationToken); + + return this.Ok(); + } + + /// + /// Deletes multiple entities by their identifiers. + /// + /// The identifiers of the entities to delete. + /// Cancellation token. + /// Void. + /// Entities deleted. + /// Bad request. + /// Unauthorized. + /// Entities not found. + /// Internal server error. + [HttpDelete] + [Route(ActionRoutes.DELETE_MANY)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DeleteManyAsync([FromBody][Required] Guid[] ids, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .DeleteManyAsync(new DeleteManyRequest + { + Ids = ids + }, cancellationToken); + + return this.Ok(); + } + + /// + /// Deletes multiple entities by their identifiers in bulk. + /// + /// The identifiers of the entities to delete. + /// Cancellation token. + /// Void. + /// Entities deleted. + /// Bad request. + /// Unauthorized. + /// Entities not found. + /// Internal server error. + [HttpDelete] + [Route(ActionRoutes.DELETE_MANY_BULK)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NotFound)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DeleteManyBulkAsync([FromBody][Required] Guid[] ids, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .DeleteManyBulkAsync(new DeleteManyBulkRequest + { + Ids = ids + }, cancellationToken); + + return this.Ok(); + } + + /// + /// Deletes entities matching the specified criteria. + /// + /// The criteria for selecting entities to delete. + /// Cancellation token. + /// Void. + /// Entities deleted. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpDelete] + [Route(ActionRoutes.DELETE_QUERY)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DeleteQueryAsync([FromBody][Required] ExampleQueryCriteria select, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .DeleteQueryAsync(new DeleteQueryRequest + { + Criteria = select + }, cancellationToken); + + return this.Ok(); + } + + /// + /// Bulk deletes entities matching the specified criteria. + /// + /// The criteria for selecting entities to delete. + /// Cancellation token. + /// Void. + /// Entities deleted. + /// Bad request. + /// Unauthorized. + /// Internal server error. + [HttpDelete] + [Route(ActionRoutes.DELETE_QUERY_BULK)] + [Consumes(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task DeleteQueryBulkAsync([FromBody][Required] ExampleQueryCriteria select, CancellationToken cancellationToken = default) + { + await this.nanoApiClient.Entity + .DeleteQueryBulkAsync(new DeleteQueryBulkRequest + { + Criteria = select + }, cancellationToken); + + return this.Ok(); + } + + #endregion +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local b/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local new file mode 100644 index 00000000..72d7216b --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ApiClients.Entity.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Program.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/Properties/InternalsVisibleTo.cs b/Api.ApiClients.Entity/Api.ApiClients.Entity/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..9a663cce --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ApiClients.Entity")] \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Development.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Production.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Staging.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json new file mode 100644 index 00000000..62eb606b --- /dev/null +++ b/Api.ApiClients.Entity/Api.ApiClients.Entity/appsettings.json @@ -0,0 +1,25 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Apis": { + "NanoApiClient": { + "Host": "api.apiclients.entity.service", + "Root": "api", + "Port": 8181, + "UseSsl": false, + "Timeout": "00:00:30", + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } + } + } +} \ No newline at end of file diff --git a/Api.ApiClients.Entity/Dockerfile b/Api.ApiClients.Entity/Dockerfile new file mode 100644 index 00000000..961dbddb --- /dev/null +++ b/Api.ApiClients.Entity/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.ApiClients.Entity.dll"] \ No newline at end of file diff --git a/Api.ApiClients.Entity/LICENSE b/Api.ApiClients.Entity/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.ApiClients.Entity/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.ApiClients.Entity/README.md b/Api.ApiClients.Entity/README.md new file mode 100644 index 00000000..e75c3c09 --- /dev/null +++ b/Api.ApiClients.Entity/README.md @@ -0,0 +1,30 @@ +# Api.ApiClients.Entity + +> _Nano API application with api-client entity models._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.ApiClients](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.ApiClients)**. All the custom methods have been removed and replaced +with corresponding methods for each entity controller operation in the inner service. + +The inner application has a data provider enabled to demonstrate the generic API client integration with Nano entity models. A `NanoApiClient` implementation, derived from +`BaseApiClient`, has been added to the service application. This lesson showcases how to use the `Entity` sub-group of the `BaseApiClient` to interact with any entity model +exposed by the inner application. + +The endpoints mirror those of the entity controller in the inner service, allowing each entity action to be invoked through the API client for demonstration purposes. In a +real-world scenario, this structure would typically differ. The outer application would define its own request and response contracts tailored to its domain. However, for +simplicity and clarity in this example, the responses from the inner service are passed directly through the outer API. + +> 📖 Learn more about **[Nano Api Clients](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App/README.md#api-clients)**. diff --git a/Api.ApiClients.Entity/icon.png b/Api.ApiClients.Entity/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/.docker/docker-compose.yml b/Api.ApiClients.RootLogIn/.docker/docker-compose.yml new file mode 100644 index 00000000..8f0c55a8 --- /dev/null +++ b/Api.ApiClients.RootLogIn/.docker/docker-compose.yml @@ -0,0 +1,29 @@ +services: + api.apiclients.rootlogin: + image: api.apiclients.rootlogin + hostname: api-apiclients-rootlogin + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.ApiClients.RootLogIn + dockerfile: "Dockerfile.Local" + networks: + - network + + api.apiclients.rootlogin.service: + image: api.apiclients.rootlogin.service + hostname: api-apiclients-rootlogin-service + restart: on-failure + ports: + - 8181:8181 + build: + context: ../Api.ApiClients.RootLogIn.Service + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.ApiClients.RootLogIn/.dockerignore b/Api.ApiClients.RootLogIn/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.ApiClients.RootLogIn/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.ApiClients.RootLogIn/.github/config/slack.yml b/Api.ApiClients.RootLogIn/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.ApiClients.RootLogIn/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml b/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..1fa27e27 --- /dev/null +++ b/Api.ApiClients.RootLogIn/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.ApiClients.RootLogIn + IMAGE_NAME: api.apiclients.rootlogin + SERVICE_NAME: api-apiclients-rootlogin + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/.gitignore b/Api.ApiClients.RootLogIn/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.ApiClients.RootLogIn/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/.kubernetes/autoscaler.yaml b/Api.ApiClients.RootLogIn/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.ApiClients.RootLogIn/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml b/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.ApiClients.RootLogIn/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml b/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.ApiClients.RootLogIn/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.ApiClients.RootLogIn/.kubernetes/service.yaml b/Api.ApiClients.RootLogIn/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.ApiClients.RootLogIn/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Properties/DoNotParallelize.cs b/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Tests.Api.ApiClients.RootLogIn.csproj b/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Tests.Api.ApiClients.RootLogIn.csproj new file mode 100644 index 00000000..faf5ada4 --- /dev/null +++ b/Api.ApiClients.RootLogIn/.tests/Tests.Api.ApiClients.RootLogIn/Tests.Api.ApiClients.RootLogIn.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.Models/Api.ApiClients.RootLogIn.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.sln b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.sln new file mode 100644 index 00000000..4b5b043d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogIn.sln @@ -0,0 +1,159 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.RootLogIn.Models", "Api.ApiClients.RootLogIn.Models\Api.ApiClients.RootLogIn.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.RootLogIn", "Api.ApiClients.RootLogIn\Api.ApiClients.RootLogIn.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ApiClients.RootLogIn", ".tests\Tests.Api.ApiClients.RootLogIn\Tests.Api.ApiClients.RootLogIn.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.RootLogIn.Service.Models", "Api.ApiClients.RootLogIn.Service.Models\Api.ApiClients.RootLogIn.Service.Models.csproj", "{72C75C0B-EACD-A113-B9A4-2600519A71E0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.RootLogIn.Service", "Api.ApiClients.RootLogIn.Service\Api.ApiClients.RootLogIn.Service.csproj", "{37CEC825-166A-5BB7-9466-A75FFE32383F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.Build.0 = Release|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/Api.ApiClients.RootLogin.Service.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/NanoApiClient.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/NanoApiClient.cs new file mode 100644 index 00000000..1c1cabe7 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/NanoApiClient.cs @@ -0,0 +1,24 @@ +using System.Threading; +using System.Threading.Tasks; +using Api.ApiClients.RootLogIn.Service.Models.ApiClient.Requests; +using Nano.App.ApiClient; +using Nano.Data.Abstractions.Identity.Authentication.Models; + +namespace Api.ApiClients.RootLogIn.Service.Models.ApiClient; + +/// +/// Nano Api Client. +/// +public class NanoApiClient(Nano.App.ApiClient.ApiClient apiClient) : BaseApiClient(apiClient) +{ + /// + /// Auto Authenticate Root Async. + /// + /// The . + /// The . + /// The . + public Task AutoAuthenticateRootAsync(AutoAuthenticateRootRequest request, CancellationToken cancellationToken = default) + { + return this.InvokeAsync(request, cancellationToken); + } +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/AutoAuthenticateRootRequest.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/AutoAuthenticateRootRequest.cs new file mode 100644 index 00000000..a1452d56 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/AutoAuthenticateRootRequest.cs @@ -0,0 +1,9 @@ +using Nano.App.ApiClient.Annotations.Actions; + +namespace Api.ApiClients.RootLogIn.Service.Models.ApiClient.Requests; + +/// +/// Auto Authenticate Root Request. +/// +[GetAction("auto-authenticate-root")] +public class AutoAuthenticateRootRequest : BaseCustomsRequest; \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/BaseCustomsRequest.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/BaseCustomsRequest.cs new file mode 100644 index 00000000..05bf4544 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service.Models/ApiClient/Requests/BaseCustomsRequest.cs @@ -0,0 +1,17 @@ +using Nano.App.ApiClient.Requests; + +namespace Api.ApiClients.RootLogIn.Service.Models.ApiClient.Requests; + +/// +/// Base Customs Request (abstract). +/// +public abstract class BaseCustomsRequest : BaseRequest +{ + /// + /// Constructor. + /// + protected BaseCustomsRequest() + { + this.Controller = "customs"; + } +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Api.ApiClients.RootLogin.Service.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Api.ApiClients.RootLogin.Service.csproj new file mode 100644 index 00000000..b2183917 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Api.ApiClients.RootLogin.Service.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/AuthController.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/AuthController.cs new file mode 100644 index 00000000..dabd8009 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/AuthController.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Api.Mvc.Authentication.Abstractions; + +namespace Api.ApiClients.RootLogIn.Service.Controllers; + +/// +public class AuthController(ILogger logger, IAuthRepository authRepository) + : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/CustomsController.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/CustomsController.cs new file mode 100644 index 00000000..5cf39f1a --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Controllers/CustomsController.cs @@ -0,0 +1,39 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions.Identity.Authentication.Models; +using Nano.Data.Abstractions.Identity.Extensions; + +namespace Api.ApiClients.RootLogIn.Service.Controllers; + +/// +/// Customs Controller. +/// +/// The . +public class CustomsController(ILogger logger) : BaseController(logger) +{ + /// + /// Auto Authenticate Root Action. + /// + /// The cancellation token. + /// The access token. + /// Success. + [HttpGet] + [Route("auto-authenticate-root")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task AutoAuthenticateRootAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + var accessToken = this.HttpContext + .GetJwtToken(); + + return this.Ok(new AccessToken + { + Token = accessToken! + }); + } +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local new file mode 100644 index 00000000..e6afa324 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Program.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Properties/InternalsVisibleTo.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..5d1d7f6d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ApiClients.RootLogin")] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Development.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Production.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Staging.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json new file mode 100644 index 00000000..f28ebab1 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin.Service/appsettings.json @@ -0,0 +1,26 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8181 + ] + } + }, + "Authentication": { + "Jwt": { + "Issuer": "Development.nano", + "Audience": "Development.nano", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", + "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV", + "Expiration": "24:00:00", + "RootLogin": { + "Username": "admin@domain.com", + "Password": "abc12|+d34DadD" + } + } + } + } +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Api.ApiClients.RootLogIn.csproj b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Api.ApiClients.RootLogIn.csproj new file mode 100644 index 00000000..aabad09a --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Api.ApiClients.RootLogIn.csproj @@ -0,0 +1,38 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Controllers/ExamplesController.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Controllers/ExamplesController.cs new file mode 100644 index 00000000..05dda3f6 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Controllers/ExamplesController.cs @@ -0,0 +1,41 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.ApiClients.RootLogIn.Service.Models.ApiClient; +using Api.ApiClients.RootLogIn.Service.Models.ApiClient.Requests; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Common.Consts; +using Nano.Data.Abstractions.Identity.Authentication.Models; + +namespace Api.ApiClients.RootLogIn.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, NanoApiClient nanoApiClient) : BaseController(logger) +{ + private readonly NanoApiClient nanoApiClient = nanoApiClient ?? throw new ArgumentNullException(nameof(nanoApiClient)); + + /// + /// Auto Authenticate Root. + /// + /// The cancellation token. + /// The access token. + /// Success. + [HttpGet] + [Route("auto-authenticate-root")] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(AccessToken), (int)HttpStatusCode.OK)] + public virtual async Task AutoAuthenticateRootAsync(CancellationToken cancellationToken = default) + { + var response = await this.nanoApiClient + .AutoAuthenticateRootAsync(new AutoAuthenticateRootRequest(), cancellationToken); + + return this.Ok(response); + } +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local new file mode 100644 index 00000000..557a434d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ApiClients.RootLogIn.dll"] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Program.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Properties/InternalsVisibleTo.cs b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..98b32be2 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ApiClients.RootLogIn")] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Development.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Production.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Staging.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.json b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.json new file mode 100644 index 00000000..81fbdd51 --- /dev/null +++ b/Api.ApiClients.RootLogIn/Api.ApiClients.RootLogin/appsettings.json @@ -0,0 +1,30 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { }, + "Apis": { + "NanoApiClient": { + "Host": "api.apiclients.rootlogin.service", + "Root": "api", + "Port": 8181, + "UseSsl": false, + "Timeout": "00:00:30", + "LogInRoot": { + "Username": "admin@domain.com", + "Password": "abc12|+d34DadD" + }, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } + } + } +} \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/Dockerfile b/Api.ApiClients.RootLogIn/Dockerfile new file mode 100644 index 00000000..2fad135c --- /dev/null +++ b/Api.ApiClients.RootLogIn/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.ApiClients.RootLogIn.dll"] \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/LICENSE b/Api.ApiClients.RootLogIn/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.ApiClients.RootLogIn/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.ApiClients.RootLogIn/README.md b/Api.ApiClients.RootLogIn/README.md new file mode 100644 index 00000000..e6cc353d --- /dev/null +++ b/Api.ApiClients.RootLogIn/README.md @@ -0,0 +1,31 @@ +# Api.ApiClients.RootLogIn + +> _Nano API application with api-client root authentication._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.ApiClients](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.ApiClients)**. All custom methods have been removed and replaced +with a new method that triggers the configured root login. + +The service is configured with Root Login and JWT authentication enabled, along with a concrete implementation of `BaseAuthController`. When invoking methods through the +API client, the automatic root login mechanism is triggered. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/auto-authenticate-root` | Returns a `200 OK` response. Uses the API client’s automatic root login to obtain and return an access token. | + +> 📖 Learn more about **[Nano Api Clients](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App/README.md#api-clients)**. diff --git a/Api.ApiClients.RootLogIn/icon.png b/Api.ApiClients.RootLogIn/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.ApiClients/.docker/docker-compose.yml b/Api.ApiClients/.docker/docker-compose.yml new file mode 100644 index 00000000..0129b3b3 --- /dev/null +++ b/Api.ApiClients/.docker/docker-compose.yml @@ -0,0 +1,29 @@ +services: + api.apiclients: + image: api.apiclients + hostname: api-apiclients + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.ApiClients + dockerfile: "Dockerfile.Local" + networks: + - network + + api.apiclients.service: + image: api.apiclients.service + hostname: api-apiclients-service + restart: on-failure + ports: + - 8181:8181 + build: + context: ../Api.ApiClients.Service + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.ApiClients/.dockerignore b/Api.ApiClients/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.ApiClients/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.ApiClients/.github/config/slack.yml b/Api.ApiClients/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.ApiClients/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ApiClients/.github/workflows/build-and-deploy.yml b/Api.ApiClients/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..f97c3744 --- /dev/null +++ b/Api.ApiClients/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.ApiClients + IMAGE_NAME: api.apiclients + SERVICE_NAME: api-apiclients + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ApiClients/.gitignore b/Api.ApiClients/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.ApiClients/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ApiClients/.kubernetes/autoscaler.yaml b/Api.ApiClients/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.ApiClients/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ApiClients/.kubernetes/configmap.yaml b/Api.ApiClients/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.ApiClients/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ApiClients/.kubernetes/deployment.yaml b/Api.ApiClients/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.ApiClients/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.ApiClients/.kubernetes/service.yaml b/Api.ApiClients/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.ApiClients/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.ApiClients/.tests/Tests.Api.ApiClients/Properties/DoNotParallelize.cs b/Api.ApiClients/.tests/Tests.Api.ApiClients/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.ApiClients/.tests/Tests.Api.ApiClients/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ApiClients/.tests/Tests.Api.ApiClients/Tests.Api.ApiClients.csproj b/Api.ApiClients/.tests/Tests.Api.ApiClients/Tests.Api.ApiClients.csproj new file mode 100644 index 00000000..2cd69c49 --- /dev/null +++ b/Api.ApiClients/.tests/Tests.Api.ApiClients/Tests.Api.ApiClients.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj b/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Models/Api.ApiClients.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj b/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api.ApiClients.Service.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/NanoApiClient.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/NanoApiClient.cs new file mode 100644 index 00000000..849b28b9 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/NanoApiClient.cs @@ -0,0 +1,92 @@ +using Nano.App.ApiClient; +using System; +using System.Threading; +using System.Threading.Tasks; +using Api.ApiClients.Service.Models.Api.Requests; +using Api.ApiClients.Service.Models.Api.Responses; + +namespace Api.ApiClients.Service.Models.Api; + +/// +/// Nano Api Client. +/// +public class NanoApiClient(ApiClient apiClient) : BaseApiClient(apiClient) +{ + /// + /// Custom Async. + /// + /// The . + /// The . + /// The . + public Task CustomAsync(CustomRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + + return this.InvokeAsync(request, cancellationToken); + } + + /// + /// Custom File Async. + /// + /// The . + /// The . + /// The . + public Task CustomFileAsync(CustomFileRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + + return this.InvokeAsync(request, cancellationToken); + } + + /// + /// Custom File Body Async. + /// + /// The . + /// The . + /// The . + public Task CustomFileBodyAsync(CustomFileBodyRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + + return this.InvokeAsync(request, cancellationToken); + } + + /// + /// Bad Request Exception Async. + /// + /// The . + /// The . + /// Nothing. + public Task BadRequestExceptionAsync(BadRequestExceptionRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + + return this.InvokeAsync(request, cancellationToken); + } + + /// + /// Problem Details Exception Async. + /// + /// The . + /// The . + /// Nothing. + public Task ProblemDetailsExceptionAsync(ProblemDetailsExceptionRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + + return this.InvokeAsync(request, cancellationToken); + } + + /// + /// Request Tracing Async. + /// + /// The . + /// The . + /// The . + public Task RequestTracingAsync(RequestTracingRequest request, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + + return this.InvokeAsync(request, cancellationToken); + } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BadRequestExceptionRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BadRequestExceptionRequest.cs new file mode 100644 index 00000000..579f9189 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BadRequestExceptionRequest.cs @@ -0,0 +1,9 @@ +using Nano.App.ApiClient.Annotations.Actions; + +namespace Api.ApiClients.Service.Models.Api.Requests; + +/// +/// Bad Request Exception Request. +/// +[GetAction("bad-request-exception")] +public class BadRequestExceptionRequest : BaseCustomsRequest; \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BaseCustomsRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BaseCustomsRequest.cs new file mode 100644 index 00000000..f1b6de94 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/BaseCustomsRequest.cs @@ -0,0 +1,17 @@ +using Nano.App.ApiClient.Requests; + +namespace Api.ApiClients.Service.Models.Api.Requests; + +/// +/// Base Customs Request (abstract). +/// +public abstract class BaseCustomsRequest : BaseRequest +{ + /// + /// Constructor. + /// + protected BaseCustomsRequest() + { + this.Controller = "customs"; + } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileBodyRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileBodyRequest.cs new file mode 100644 index 00000000..a8902bdf --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileBodyRequest.cs @@ -0,0 +1,25 @@ +using Api.ApiClients.Service.Models.Api.Requests.Models; +using Nano.App.ApiClient.Annotations; +using Nano.App.ApiClient.Annotations.Actions; +using Nano.App.ApiClient.Models; + +namespace Api.ApiClients.Service.Models.Api.Requests; + +/// +/// Custom File Body Request. +/// +[PostAction("custom/file/body")] +public class CustomFileBodyRequest : BaseCustomsRequest +{ + /// + /// File. + /// + [Form] + public required NamedStream File { get; set; } + + /// + /// Body. + /// + [Form] + public required CustomFileBody Body { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileRequest.cs new file mode 100644 index 00000000..0793c7c7 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomFileRequest.cs @@ -0,0 +1,18 @@ +using Nano.App.ApiClient.Annotations; +using Nano.App.ApiClient.Annotations.Actions; +using Nano.App.ApiClient.Models; + +namespace Api.ApiClients.Service.Models.Api.Requests; + +/// +/// Custom File Request. +/// +[PostAction("custom/file")] +public class CustomFileRequest : BaseCustomsRequest +{ + /// + /// File. + /// + [Form] + public required NamedStream File { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomRequest.cs new file mode 100644 index 00000000..f5d5471e --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/CustomRequest.cs @@ -0,0 +1,43 @@ +using System; +using Api.ApiClients.Service.Models.Api.Requests.Models; +using Nano.App.ApiClient.Annotations; +using Nano.App.ApiClient.Annotations.Actions; + +namespace Api.ApiClients.Service.Models.Api.Requests; + +/// +/// Custom Request. +/// +[PostAction("{id}/{type}")] +public class CustomRequest : BaseCustomsRequest +{ + /// + /// Id. + /// + [Route(Order = 0)] + public required Guid Id { get; set; } + + /// + /// Type. + /// + [Route(Order = 1)] + public required string Type { get; set; } + + /// + /// Body. + /// + [Body] + public required CustomBody Body { get; set; } + + /// + /// Query. + /// + [Query] + public required DateTimeOffset Query { get; set; } + + /// + /// Header. + /// + [Header(Name = "X-Custom-Header")] + public required string Header { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomBody.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomBody.cs new file mode 100644 index 00000000..7b46ad3b --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomBody.cs @@ -0,0 +1,12 @@ +namespace Api.ApiClients.Service.Models.Api.Requests.Models; + +/// +/// Custom Body. +/// +public class CustomBody +{ + /// + /// Name. + /// + public string? Name { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomFileBody.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomFileBody.cs new file mode 100644 index 00000000..3ba7f0e3 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/Models/CustomFileBody.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace Api.ApiClients.Service.Models.Api.Requests.Models; + +/// +/// Custom File Body. +/// +public class CustomFileBody +{ + /// + /// Text. + /// + [Required] + public string Text { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/ProblemDetailsExceptionRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/ProblemDetailsExceptionRequest.cs new file mode 100644 index 00000000..a4d9ac00 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/ProblemDetailsExceptionRequest.cs @@ -0,0 +1,9 @@ +using Nano.App.ApiClient.Annotations.Actions; + +namespace Api.ApiClients.Service.Models.Api.Requests; + +/// +/// Problem Details Exception Request. +/// +[GetAction("problem-details-exception")] +public class ProblemDetailsExceptionRequest : BaseCustomsRequest; \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/RequestTracingRequest.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/RequestTracingRequest.cs new file mode 100644 index 00000000..f39993d8 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Requests/RequestTracingRequest.cs @@ -0,0 +1,9 @@ +using Nano.App.ApiClient.Annotations.Actions; + +namespace Api.ApiClients.Service.Models.Api.Requests; + +/// +/// Problem Details Exception Request. +/// +[GetAction("request-tracing")] +public class RequestTracingRequest : BaseCustomsRequest; \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileBodyResponse.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileBodyResponse.cs new file mode 100644 index 00000000..b87275f6 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileBodyResponse.cs @@ -0,0 +1,14 @@ +using Api.ApiClients.Service.Models.Api.Requests.Models; + +namespace Api.ApiClients.Service.Models.Api.Responses; + +/// +/// Custom File Body Response. +/// +public class CustomFileBodyResponse : CustomFileResponse +{ + /// + /// Body. + /// + public required CustomFileBody Body { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileResponse.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileResponse.cs new file mode 100644 index 00000000..81e80282 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomFileResponse.cs @@ -0,0 +1,12 @@ +namespace Api.ApiClients.Service.Models.Api.Responses; + +/// +/// Custom File Response. +/// +public class CustomFileResponse +{ + /// + /// Filename. + /// + public required string Filename { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomResponse.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomResponse.cs new file mode 100644 index 00000000..2288031c --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/CustomResponse.cs @@ -0,0 +1,36 @@ +using Api.ApiClients.Service.Models.Api.Requests; +using System; +using Api.ApiClients.Service.Models.Api.Requests.Models; + +namespace Api.ApiClients.Service.Models.Api.Responses; + +/// +/// Custom Response. +/// +public class CustomResponse +{ + /// + /// Id. + /// + public required Guid Id { get; set; } + + /// + /// Type. + /// + public required string Type { get; set; } + + /// + /// Body. + /// + public required CustomBody Body { get; set; } + + /// + /// Query. + /// + public required DateTimeOffset? Query { get; set; } + + /// + /// Header. + /// + public required string Header { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/RequestTracingResponse.cs b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/RequestTracingResponse.cs new file mode 100644 index 00000000..ef71234a --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service.Models/Api/Responses/RequestTracingResponse.cs @@ -0,0 +1,12 @@ +namespace Api.ApiClients.Service.Models.Api.Responses; + +/// +/// Request Tracing Response. +/// +public class RequestTracingResponse +{ + /// + /// Request Id. + /// + public required string RequestId { get; set; } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/Api.ApiClients.Service.csproj b/Api.ApiClients/Api.ApiClients.Service/Api.ApiClients.Service.csproj new file mode 100644 index 00000000..6e1bedf1 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/Api.ApiClients.Service.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/Controllers/CustomsController.cs b/Api.ApiClients/Api.ApiClients.Service/Controllers/CustomsController.cs new file mode 100644 index 00000000..7c82ca0c --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/Controllers/CustomsController.cs @@ -0,0 +1,158 @@ +using Api.ApiClients.Service.Models.Api.Responses; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Annotations; +using Nano.App.Api.Controllers; +using Nano.App.Exceptions; +using Nano.Common.Consts; +using System; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.ApiClients.Service.Models.Api.Requests.Models; + +namespace Api.ApiClients.Service.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class CustomsController(ILogger logger) : BaseController(logger) +{ + /// + /// Custom Action. + /// + /// The first route parameter. + /// The second route parameter. + /// The request body. + /// The querystring parameter. + /// The header value + /// The cancellation token. + /// A custom response. + /// Success. + [HttpPost] + [Route("{id:guid}/{type}")] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(CustomResponse), (int)HttpStatusCode.OK)] + public virtual async Task CustomAsync([FromRoute][Required] Guid id, [FromRoute][Required] string type, [FromBody][Required] CustomBody body, [FromQuery][Required] DateTimeOffset query, [FromHeader(Name = "X-Custom-Header")][Required] string header, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok(new CustomResponse + { + Id = id, + Type = type, + Body = body, + Query = query, + Header = header + }); + } + + /// + /// Custom File Action. + /// + /// The file. + /// The cancellation token. + /// The custom file response. + /// Success. + [HttpPost] + [Route("custom/file")] + [Consumes(HttpContentType.FORM)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(CustomFileResponse), (int)HttpStatusCode.OK)] + public virtual async Task CustomFileAsync(IFormFile file, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok(new CustomFileResponse + { + Filename = file.FileName + }); + } + + /// + /// Custom File Body Action. + /// + /// The file. + /// The json body + /// The cancellation token. + /// The custom file body response. + /// Success. + [HttpPost] + [Route("custom/file/body")] + [Consumes(HttpContentType.FORM)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(CustomFileBodyResponse), (int)HttpStatusCode.OK)] + public virtual async Task CustomFileBodyAsync(IFormFile file, [Required][FromFormBody] CustomFileBody body, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok(new CustomFileBodyResponse + { + Filename = file.FileName, + Body = body + }); + } + + /// + /// Custom Bad Request Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Bad Request. + [HttpGet] + [Route("bad-request-exception")] + [Produces(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public virtual async Task CustomBadRequestExceptionAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new BadRequestException("bad request.", true, true); + } + + /// + /// Custom Problem Details Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Expectation Failed. + [HttpGet] + [Route("problem-details-exception")] + [Produces(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.ExpectationFailed)] + public virtual async Task CustomProblemDetailsExceptionAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new ProblemDetailsException(new ProblemDetails + { + Type = "https://datatracker.ietf.org/doc/html/rfc9110#name-417-expectation-failed", + Title = "Expectation Failed", + Status = (int)HttpStatusCode.ExpectationFailed + }); + } + + /// + /// Request Tracing Action. + /// + /// The cancellation token. + /// The request tracing response. + /// Success. + [HttpGet] + [Route("request-tracing")] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(RequestTracingResponse), (int)HttpStatusCode.OK)] + public virtual async Task RequestTracingAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok(new RequestTracingResponse + { + RequestId = this.HttpContext.TraceIdentifier + }); + } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local b/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local new file mode 100644 index 00000000..5d914dde --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ApiClients.Service.dll"] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/Program.cs b/Api.ApiClients/Api.ApiClients.Service/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ApiClients/Api.ApiClients.Service/Properties/InternalsVisibleTo.cs b/Api.ApiClients/Api.ApiClients.Service/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..e3560176 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ApiClients")] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.Development.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.Production.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.Staging.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.Service/appsettings.json b/Api.ApiClients/Api.ApiClients.Service/appsettings.json new file mode 100644 index 00000000..702a5e62 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.Service/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8181 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients.sln b/Api.ApiClients/Api.ApiClients.sln new file mode 100644 index 00000000..3d35e9ea --- /dev/null +++ b/Api.ApiClients/Api.ApiClients.sln @@ -0,0 +1,159 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Models", "Api.ApiClients.Models\Api.ApiClients.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients", "Api.ApiClients\Api.ApiClients.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ApiClients", ".tests\Tests.Api.ApiClients\Tests.Api.ApiClients.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Service.Models", "Api.ApiClients.Service.Models\Api.ApiClients.Service.Models.csproj", "{72C75C0B-EACD-A113-B9A4-2600519A71E0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ApiClients.Service", "Api.ApiClients.Service\Api.ApiClients.Service.csproj", "{37CEC825-166A-5BB7-9466-A75FFE32383F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72C75C0B-EACD-A113-B9A4-2600519A71E0}.Release|Any CPU.Build.0 = Release|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {37CEC825-166A-5BB7-9466-A75FFE32383F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.ApiClients/Api.ApiClients/Api.ApiClients.csproj b/Api.ApiClients/Api.ApiClients/Api.ApiClients.csproj new file mode 100644 index 00000000..30feaf81 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/Api.ApiClients.csproj @@ -0,0 +1,38 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/Controllers/ExamplesController.cs b/Api.ApiClients/Api.ApiClients/Controllers/ExamplesController.cs new file mode 100644 index 00000000..cfc94ee1 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/Controllers/ExamplesController.cs @@ -0,0 +1,184 @@ +using Api.ApiClients.Service.Models.Api; +using Api.ApiClients.Service.Models.Api.Requests; +using Api.ApiClients.Service.Models.Api.Requests.Models; +using Api.ApiClients.Service.Models.Api.Responses; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Annotations; +using Nano.App.Api.Controllers; +using Nano.Common.Consts; +using System; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Nano.App.ApiClient.Models; + +namespace Api.ApiClients.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, NanoApiClient nanoApiClient) : BaseController(logger) +{ + private readonly NanoApiClient nanoApiClient = nanoApiClient ?? throw new ArgumentNullException(nameof(nanoApiClient)); + + /// + /// Custom. + /// + /// The first route parameter. + /// The second route parameter. + /// The request body. + /// The querystring parameter. + /// The header value + /// The cancellation token. + /// The custom response. + /// Success. + [HttpPost] + [Route("custom/{id:guid}/{type}")] + [Consumes(HttpContentType.JSON)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(CustomResponse), (int)HttpStatusCode.OK)] + public virtual async Task CustomAsync([FromRoute][Required] Guid id, [FromRoute][Required] string type, [FromBody][Required] CustomBody body, [FromQuery][Required] DateTimeOffset query, [FromHeader(Name = "X-Custom-Header")][Required] string header, CancellationToken cancellationToken = default) + { + var response = await this.nanoApiClient + .CustomAsync(new CustomRequest + { + Id = id, + Type = type, + Body = body, + Query = query, + Header = header + }, cancellationToken); + + if (response == null) + { + return this.NotFound(); + } + + return this.Ok(response); + } + + /// + /// Custom File Action. + /// + /// The file. + /// The cancellation token. + /// The custom file response. + /// Success. + [HttpPost] + [Route("custom/file")] + [Consumes(HttpContentType.FORM)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(CustomFileResponse), (int)HttpStatusCode.OK)] + public virtual async Task CustomFileAsync(IFormFile file, CancellationToken cancellationToken = default) + { + var response = await this.nanoApiClient + .CustomFileAsync(new CustomFileRequest + { + File = new NamedStream + { + Name = file.FileName, + Stream = file.OpenReadStream() + } + }, cancellationToken); + + if (response == null) + { + return this.NotFound(); + } + + return this.Ok(response); + } + + /// + /// Custom File Body Action. + /// + /// The file. + /// The json body + /// The cancellation token. + /// The custom file body response. + /// Success. + [HttpPost] + [Route("custom/file/body")] + [Consumes(HttpContentType.FORM)] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(CustomFileResponse), (int)HttpStatusCode.OK)] + public virtual async Task CustomFileBodyAsync(IFormFile file, [Required][FromFormBody] CustomFileBody body, CancellationToken cancellationToken = default) + { + var response = await this.nanoApiClient + .CustomFileBodyAsync(new CustomFileBodyRequest + { + File = new NamedStream + { + Name = file.FileName, + Stream = file.OpenReadStream() + }, + Body = body + }, cancellationToken); + + if (response == null) + { + return this.NotFound(); + } + + return this.Ok(response); + } + + /// + /// Bad Request. + /// + /// The cancellation token. + /// Nothings. + /// Bad Request. + [HttpGet] + [Route("bad-request-exception")] + [Produces(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public virtual async Task BadRequestAsync(CancellationToken cancellationToken = default) + { + await this.nanoApiClient + .BadRequestExceptionAsync(new BadRequestExceptionRequest(), cancellationToken); + + return this.Ok(); + } + + /// + /// Problem Details Exception. + /// + /// The cancellation token. + /// Nothings. + /// Expectation Failed. + [HttpGet] + [Route("problem-details-exception")] + [Produces(HttpContentType.JSON)] + [ProducesResponseType((int)HttpStatusCode.ExpectationFailed)] + public virtual async Task ProblemDetailsExceptionAsync(CancellationToken cancellationToken = default) + { + await this.nanoApiClient + .ProblemDetailsExceptionAsync(new ProblemDetailsExceptionRequest(), cancellationToken); + + return this.Ok(); + } + + /// + /// Request Tracing Exception. + /// + /// The cancellation token. + /// The request tracing response. + /// Success. + [HttpGet] + [Route("request-tracing")] + [Produces(HttpContentType.JSON)] + [ProducesResponseType(typeof(RequestTracingResponse), (int)HttpStatusCode.OK)] + public virtual async Task RequestTracingAsync(CancellationToken cancellationToken = default) + { + var response = await this.nanoApiClient + .RequestTracingAsync(new RequestTracingRequest(), cancellationToken); + + return this.Ok(response); + } +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/Dockerfile.Local b/Api.ApiClients/Api.ApiClients/Dockerfile.Local new file mode 100644 index 00000000..ce847262 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ApiClients.dll"] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/Program.cs b/Api.ApiClients/Api.ApiClients/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ApiClients/Api.ApiClients/Properties/InternalsVisibleTo.cs b/Api.ApiClients/Api.ApiClients/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..e3560176 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ApiClients")] \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/appsettings.Development.json b/Api.ApiClients/Api.ApiClients/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/appsettings.Production.json b/Api.ApiClients/Api.ApiClients/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/appsettings.Staging.json b/Api.ApiClients/Api.ApiClients/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ApiClients/Api.ApiClients/appsettings.json b/Api.ApiClients/Api.ApiClients/appsettings.json new file mode 100644 index 00000000..07cd49d9 --- /dev/null +++ b/Api.ApiClients/Api.ApiClients/appsettings.json @@ -0,0 +1,26 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { }, + "Apis": { + "NanoApiClient": { + "Host": "api.apiclients.service", + "Root": "api", + "Port": 8181, + "UseSsl": false, + "Timeout": "00:00:30", + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } + } + } +} \ No newline at end of file diff --git a/Api.ApiClients/Dockerfile b/Api.ApiClients/Dockerfile new file mode 100644 index 00000000..554c9c3f --- /dev/null +++ b/Api.ApiClients/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.ApiClients.dll"] \ No newline at end of file diff --git a/Api.ApiClients/LICENSE b/Api.ApiClients/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.ApiClients/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.ApiClients/README.md b/Api.ApiClients/README.md new file mode 100644 index 00000000..6e3af283 --- /dev/null +++ b/Api.ApiClients/README.md @@ -0,0 +1,64 @@ +# Api.ApiClients + +> _Nano API application with api-clients._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)**. + +This lesson demonstrates how to connect one API application to another using Nano’s built-in API client, enabling seamless communication between services. + +A `NanoApiClient` implementation, derived from `BaseApiClient`, has been added to the inner service application. It includes several methods that demonstrate different +features of the Nano API client. These methods are in turn exposed through corresponding endpoints in the `ExamplesController` of the outer API application. In addition, +the outer application has been configured to include the API client, enabling it to communicate with the inner service application. + +A health check is configured to target the application of the api-client. +Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#health-checks)**. + +The following endpoint is available for testing. + +| Endpoint | Description | +| -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/custom/{id:guid}/{type}` | Returns a `200 OK` response. The provided route, query, header, and body variables are forwarded to the inner service and returned in the response. | +| `http://localhost:8080/api/examples/custom/file` | Returns a `200 OK` response. A file is uploaded via the API client, and the uploaded filename is returned in the response. | +| `http://localhost:8080/api/examples/custom/file/body` | Returns a `200 OK` response. A file and form data are uploaded via the API client, and both the filename and request body are returned in the response. | +| `http://localhost:8080/api/examples/bad-request-exception` | Returns a `400 Bad Request` response. A `BadRequestException` is intentionally thrown to demonstrate error handling. | +| `http://localhost:8080/api/examples/problem-details-exception` | Returns a `417 Expectation Failed` response. A `ProblemDetailsException` is thrown to demonstrate structured error handling using Problem Details. | +| `http://localhost:8080/api/examples/request-tracing` | Returns a `200 OK` response. The `X-Request-Id` header is extracted from the request and returned in the response for traceability purposes. | + +> 📖 Learn more about **[Nano Api Clients](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App/README.md#api-clients)**. + +## Configuration +Configured the application with a connection to the `NanoApiClient`. + +```json +"App": { + "Apis": { + "NanoApiClient": { + "Host": "localhost", + "Root": "api", + "Port": 8181, + "UseSsl": false, + "Timeout": "00:00:30", + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } + } +} +``` diff --git a/Api.ApiClients/icon.png b/Api.ApiClients/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Auth.External.Custom/.docker/docker-compose.yml b/Api.Auth.External.Custom/.docker/docker-compose.yml new file mode 100644 index 00000000..c81609f9 --- /dev/null +++ b/Api.Auth.External.Custom/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.auth.external.custom: + image: api.auth.external.custom + hostname: api-auth-external-custom + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Auth.External.Custom + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Auth.External.Custom/.dockerignore b/Api.Auth.External.Custom/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Auth.External.Custom/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Auth.External.Custom/.github/config/slack.yml b/Api.Auth.External.Custom/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Auth.External.Custom/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml b/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..1b1ae5fa --- /dev/null +++ b/Api.Auth.External.Custom/.github/workflows/build-and-deploy.yml @@ -0,0 +1,188 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Auth.External.Custom + IMAGE_NAME: api.auth.external.custom + SERVICE_NAME: api-auth-external-custom + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/auth-jwt-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-jwt-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-jwt-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Auth.External.Custom/.gitignore b/Api.Auth.External.Custom/.gitignore new file mode 100644 index 00000000..ccb0126f --- /dev/null +++ b/Api.Auth.External.Custom/.gitignore @@ -0,0 +1,10 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env \ No newline at end of file diff --git a/Api.Auth.External.Custom/.kubernetes/auth-jwt-secret.yaml b/Api.Auth.External.Custom/.kubernetes/auth-jwt-secret.yaml new file mode 100644 index 00000000..3898973f --- /dev/null +++ b/Api.Auth.External.Custom/.kubernetes/auth-jwt-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: auth-jwt-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + jwt-public-key: %AUTH_JWT_PUBLIC_KEY% + jwt-private-key: %AUTH_JWT_PRIVATE_KEY% + diff --git a/Api.Auth.External.Custom/.kubernetes/autoscaler.yaml b/Api.Auth.External.Custom/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Auth.External.Custom/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Auth.External.Custom/.kubernetes/configmap.yaml b/Api.Auth.External.Custom/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Auth.External.Custom/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Auth.External.Custom/.kubernetes/deployment.yaml b/Api.Auth.External.Custom/.kubernetes/deployment.yaml new file mode 100644 index 00000000..aca5889f --- /dev/null +++ b/Api.Auth.External.Custom/.kubernetes/deployment.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Auth.External.Custom/.kubernetes/service.yaml b/Api.Auth.External.Custom/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Auth.External.Custom/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Properties/DoNotParallelize.cs b/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Tests.Api.Auth.External.Custom.csproj b/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Tests.Api.Auth.External.Custom.csproj new file mode 100644 index 00000000..9609bb01 --- /dev/null +++ b/Api.Auth.External.Custom/.tests/Tests.Api.Auth.External.Custom/Tests.Api.Auth.External.Custom.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj b/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom.Models/Api.Auth.External.Custom.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom.sln b/Api.Auth.External.Custom/Api.Auth.External.Custom.sln new file mode 100644 index 00000000..94dae08c --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-jwt-secret.yaml = .kubernetes\auth-jwt-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Auth.External.Custom.Models", "Api.Auth.External.Custom.Models\Api.Auth.External.Custom.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Auth.External.Custom", "Api.Auth.External.Custom\Api.Auth.External.Custom.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Auth.External.Custom", ".tests\Tests.Api.Auth.External.Custom\Tests.Api.Auth.External.Custom.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Api.Auth.External.Custom.csproj b/Api.Auth.External.Custom/Api.Auth.External.Custom/Api.Auth.External.Custom.csproj new file mode 100644 index 00000000..7c314376 --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/Api.Auth.External.Custom.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs new file mode 100644 index 00000000..0cefcb5d --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs @@ -0,0 +1,44 @@ +using System.Threading; +using System.Threading.Tasks; +using Nano.App.Api.Mvc.Authentication; +using Nano.Data.Abstractions.Identity.Authentication.Models; + +namespace Api.Auth.External.Custom.Authentication; + +/// +public class ExternalProviderCustomRepository() : BaseAuthExternalRepository("Custom") +{ + /// + public override async Task AuthenticateAsync(ImplicitFlow flow, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return new ExternalAuthenticationData + { + Id = "external-id", + Username = "MyUser", + EmailAddress = "johndoe@domain.com", + PhoneNumber = "+4520111112", + Name = "John Doe", + ExternalToken = new ExternalAuthenticationToken + { + Name = this.ProviderName, + Token = "token", + RefreshToken = "refresh-token" + } + }; + } + + /// + public override async Task AuthenticateRefreshAsync(string refreshToken, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return new ExternalAuthenticationToken + { + Name = this.ProviderName, + Token = "token", + RefreshToken = "refresh-token" + }; + } +} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/AuthController.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/AuthController.cs new file mode 100644 index 00000000..27db3eba --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/AuthController.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Api.Mvc.Authentication.Abstractions; + +namespace Api.Auth.External.Custom.Controllers; + +/// +public class AuthController(ILogger logger, IAuthRepository authRepository) + : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/ExamplesController.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/ExamplesController.cs new file mode 100644 index 00000000..f466e1d1 --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Auth.External.Custom.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Authenticated Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("authenticated")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task AuthenticatedAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("authenticated"); + } +} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local b/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local new file mode 100644 index 00000000..4d00259a --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Program.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/Properties/InternalsVisibleTo.cs b/Api.Auth.External.Custom/Api.Auth.External.Custom/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..e194e1bf --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Auth.External.Custom")] \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Development.json b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Development.json new file mode 100644 index 00000000..90d97ba5 --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Development.json @@ -0,0 +1,22 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Authentication": { + "Jwt": { + "Issuer": "Development.nano", + "Audience": "Development.nano", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", + "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV", + "Expiration": "24:00:00" + } + } + } +} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Production.json b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Staging.json b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.json b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.json new file mode 100644 index 00000000..1af6db64 --- /dev/null +++ b/Api.Auth.External.Custom/Api.Auth.External.Custom/appsettings.json @@ -0,0 +1,25 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Documentation": { + }, + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + } + } +} \ No newline at end of file diff --git a/Api.Auth.External.Custom/Dockerfile b/Api.Auth.External.Custom/Dockerfile new file mode 100644 index 00000000..2a07e09b --- /dev/null +++ b/Api.Auth.External.Custom/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Auth.External.Custom/LICENSE b/Api.Auth.External.Custom/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Auth.External.Custom/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Auth.External.Custom/README.md b/Api.Auth.External.Custom/README.md new file mode 100644 index 00000000..dc8fc4a4 --- /dev/null +++ b/Api.Auth.External.Custom/README.md @@ -0,0 +1,116 @@ +# Api.Auth.External.Custom + +> _Nano API application with transient custom external authentication._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#summary) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a derived `AuthController` as well as a simple test controller +that inherits from the top-level Nano `BaseController`. + +The JWT authentication scheme has been configured, and a `BaseAuthExternalRepository` implementation named `ExternalProviderCustomRepository`, with `Custom` as provider-name, +has been added. As a result, additional endpoints from the `AuthController` are now exposed, as shown below. + +The `ExternalProviderCustomRepository` always succeeds and returns static (mocked) data. It serves as a simple example of how to implement a custom external authentication +provider in Nano. + +API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration +are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +The following endpoint from the auth controller is available for testing. + +| Endpoint | Description | +| ----------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| `http://localhost:8080/api/auth/external/schemes` | Retrieves all configured external authentication methods, e.g., Google, Facebook. | +| `http://localhost:8080/api/auth/login/external/custom/transient` | Signs in a user using external custom authentication transient | + +Additionally, the following endpoint is available for testing authorization. + +| Endpoint | Description | +| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/authenticate` | Returns a simple `200 OK` response, when JWT authorization is successful, and otherwise a `401 Unauthorized`. | + +> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#authentication)**. + +## Configuration +Configured the application with the necessary authentication setup. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + } +} +``` + +...and `appsettings.Development.json`. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "Development.nano", + "Audience": "Development.nano", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", + "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", + "Expiration": "24:00:00" + } + } +} +``` + +## Kubernetes +For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be +enabled. Below demonstrates how to map the secret containing the JWT keys. + +```yaml +spec: + template: + spec: + containers: + env: + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key +``` + +## GitHub Action +The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. + +```yaml +env: + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} +``` + +...and created during the Kubernetes deploy step. diff --git a/Api.Auth.External.Custom/icon.png b/Api.Auth.External.Custom/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Auth.RootLogin/.docker/docker-compose.yml b/Api.Auth.RootLogin/.docker/docker-compose.yml new file mode 100644 index 00000000..312e415c --- /dev/null +++ b/Api.Auth.RootLogin/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.auth.rootlogin: + image: api.auth.rootlogin + hostname: api-auth-rootlogin + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Auth.RootLogin + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Auth.RootLogin/.dockerignore b/Api.Auth.RootLogin/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Auth.RootLogin/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Auth.RootLogin/.github/config/slack.yml b/Api.Auth.RootLogin/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Auth.RootLogin/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml b/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..b33ac0de --- /dev/null +++ b/Api.Auth.RootLogin/.github/workflows/build-and-deploy.yml @@ -0,0 +1,188 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Auth.RootLogin + IMAGE_NAME: api.auth.rootlogin + SERVICE_NAME: api-auth-rootlogin + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/auth-jwt-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-jwt-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-jwt-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Auth.RootLogin/.gitignore b/Api.Auth.RootLogin/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Auth.RootLogin/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Auth.RootLogin/.kubernetes/auth-jwt-secret.yaml b/Api.Auth.RootLogin/.kubernetes/auth-jwt-secret.yaml new file mode 100644 index 00000000..3898973f --- /dev/null +++ b/Api.Auth.RootLogin/.kubernetes/auth-jwt-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: auth-jwt-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + jwt-public-key: %AUTH_JWT_PUBLIC_KEY% + jwt-private-key: %AUTH_JWT_PRIVATE_KEY% + diff --git a/Api.Auth.RootLogin/.kubernetes/autoscaler.yaml b/Api.Auth.RootLogin/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Auth.RootLogin/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Auth.RootLogin/.kubernetes/configmap.yaml b/Api.Auth.RootLogin/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Auth.RootLogin/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Auth.RootLogin/.kubernetes/deployment.yaml b/Api.Auth.RootLogin/.kubernetes/deployment.yaml new file mode 100644 index 00000000..aca5889f --- /dev/null +++ b/Api.Auth.RootLogin/.kubernetes/deployment.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Auth.RootLogin/.kubernetes/service.yaml b/Api.Auth.RootLogin/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Auth.RootLogin/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Properties/DoNotParallelize.cs b/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Tests.Api.Auth.RootLogin.csproj b/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Tests.Api.Auth.RootLogin.csproj new file mode 100644 index 00000000..78fc26d5 --- /dev/null +++ b/Api.Auth.RootLogin/.tests/Tests.Api.Auth.RootLogin/Tests.Api.Auth.RootLogin.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj b/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin.Models/Api.Auth.RootLogin.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin.sln b/Api.Auth.RootLogin/Api.Auth.RootLogin.sln new file mode 100644 index 00000000..6bfe46a0 --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-jwt-secret.yaml = .kubernetes\auth-jwt-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Auth.RootLogin.Models", "Api.Auth.RootLogin.Models\Api.Auth.RootLogin.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Auth.RootLogin", "Api.Auth.RootLogin\Api.Auth.RootLogin.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Auth.RootLogin", ".tests\Tests.Api.Auth.RootLogin\Tests.Api.Auth.RootLogin.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Api.Auth.RootLogin.csproj b/Api.Auth.RootLogin/Api.Auth.RootLogin/Api.Auth.RootLogin.csproj new file mode 100644 index 00000000..f3a63dc6 --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/Api.Auth.RootLogin.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/AuthController.cs b/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/AuthController.cs new file mode 100644 index 00000000..67904398 --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/AuthController.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Api.Mvc.Authentication.Abstractions; + +namespace Api.Auth.RootLogin.Controllers; + +/// +public class AuthController(ILogger logger, IAuthRepository authRepository) + : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/ExamplesController.cs b/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/ExamplesController.cs new file mode 100644 index 00000000..3d9a508d --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Auth.RootLogin.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Authenticated Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("authenticated")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task AuthenticatedAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("authenticated"); + } +} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local b/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local new file mode 100644 index 00000000..d87906a5 --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.dll"] \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Program.cs b/Api.Auth.RootLogin/Api.Auth.RootLogin/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/Properties/InternalsVisibleTo.cs b/Api.Auth.RootLogin/Api.Auth.RootLogin/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..540319fc --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Auth.RootLogin")] \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Development.json b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Development.json new file mode 100644 index 00000000..35df44e4 --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Development.json @@ -0,0 +1,17 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "Development.nano", + "Audience": "Development.nano", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", + "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV", + "Expiration": "24:00:00", + "RootLogin": { + "Username": "admin@domain.com", + "Password": "abc12|+d34DadD" + } + } + } + } +} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Production.json b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Staging.json b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.json b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.json new file mode 100644 index 00000000..1af6db64 --- /dev/null +++ b/Api.Auth.RootLogin/Api.Auth.RootLogin/appsettings.json @@ -0,0 +1,25 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Documentation": { + }, + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + } + } +} \ No newline at end of file diff --git a/Api.Auth.RootLogin/Dockerfile b/Api.Auth.RootLogin/Dockerfile new file mode 100644 index 00000000..3330579b --- /dev/null +++ b/Api.Auth.RootLogin/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Auth.RootLogin.dll"] \ No newline at end of file diff --git a/Api.Auth.RootLogin/LICENSE b/Api.Auth.RootLogin/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Auth.RootLogin/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Auth.RootLogin/README.md b/Api.Auth.RootLogin/README.md new file mode 100644 index 00000000..a7c64602 --- /dev/null +++ b/Api.Auth.RootLogin/README.md @@ -0,0 +1,124 @@ +# Api.Auth.RootLogin + +> _Nano API application with root login authentication._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#summary) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a derived `AuthController` as well as a simple test controller +that inherits from the top-level Nano `BaseController`. + +The JWT authentication scheme has been configured. Simply invoke the root login endpoint and use the returned JWT token in the `Authorization` header to authenticate when calling +the example controller endpoint. + +API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration +are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +The following endpoint from the auth controller is available for testing. + +| Endpoint | Description | +| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/auth/login/root` | Logins with the root credentials from configuration, and returns a simple `200 OK` response with a JWT token. | + +Additionally, the following endpoint is available for testing authorization. + +| Endpoint | Description | +| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/authenticate` | Returns a simple `200 OK` response, when JWT authorization is successful, and otherwise a `401 Unauthorized`. | + +> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#authentication)**. + +## Configuration +Configured the application with the necessary authentication setup. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + } +} +``` + +...and `appsettings.Development.json`. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "Development.nano", + "Audience": "Development.nano", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", + "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", + "Expiration": "24:00:00", + "RootLogin": { + "Username": "admin@domain.com", + "Password": "abc12|+d34DadD" + } + } + } +} +``` + +## Kubernetes +For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be +enabled. Below demonstrates how to map the secret containing the JWT keys. + +```yaml +spec: + template: + spec: + containers: + env: + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key +``` + +## GitHub Action +The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. + +```yaml +env: + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} +``` + +...and created during the Kubernetes deploy step. + +```yaml +sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; +if ($LastExitCode -ne 0) +{ + throw "error"; +}; +``` diff --git a/Api.Auth.RootLogin/icon.png b/Api.Auth.RootLogin/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Authorization/.docker/docker-compose.yml b/Api.Authorization/.docker/docker-compose.yml new file mode 100644 index 00000000..b0511a39 --- /dev/null +++ b/Api.Authorization/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.authorization: + image: api.authorization + hostname: api-authorization + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Authorization + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Authorization/.dockerignore b/Api.Authorization/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Authorization/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Authorization/.github/config/slack.yml b/Api.Authorization/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Authorization/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Authorization/.github/workflows/build-and-deploy.yml b/Api.Authorization/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..27a37e6b --- /dev/null +++ b/Api.Authorization/.github/workflows/build-and-deploy.yml @@ -0,0 +1,188 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Authorization + IMAGE_NAME: api.authorization + SERVICE_NAME: api-authorization + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/auth-jwt-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-jwt-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-jwt-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Authorization/.gitignore b/Api.Authorization/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Authorization/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Authorization/.kubernetes/auth-jwt-secret.yaml b/Api.Authorization/.kubernetes/auth-jwt-secret.yaml new file mode 100644 index 00000000..3898973f --- /dev/null +++ b/Api.Authorization/.kubernetes/auth-jwt-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: auth-jwt-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + jwt-public-key: %AUTH_JWT_PUBLIC_KEY% + jwt-private-key: %AUTH_JWT_PRIVATE_KEY% + diff --git a/Api.Authorization/.kubernetes/autoscaler.yaml b/Api.Authorization/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Authorization/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Authorization/.kubernetes/configmap.yaml b/Api.Authorization/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Authorization/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Authorization/.kubernetes/deployment.yaml b/Api.Authorization/.kubernetes/deployment.yaml new file mode 100644 index 00000000..aca5889f --- /dev/null +++ b/Api.Authorization/.kubernetes/deployment.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Authorization/.kubernetes/service.yaml b/Api.Authorization/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Authorization/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Authorization/.tests/Tests.Api.Authorization/Properties/DoNotParallelize.cs b/Api.Authorization/.tests/Tests.Api.Authorization/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Authorization/.tests/Tests.Api.Authorization/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Authorization/.tests/Tests.Api.Authorization/Tests.Api.Authorization.csproj b/Api.Authorization/.tests/Tests.Api.Authorization/Tests.Api.Authorization.csproj new file mode 100644 index 00000000..b96cdb94 --- /dev/null +++ b/Api.Authorization/.tests/Tests.Api.Authorization/Tests.Api.Authorization.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj b/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Authorization/Api.Authorization.Models/Api.Authorization.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization.sln b/Api.Authorization/Api.Authorization.sln new file mode 100644 index 00000000..0deccbeb --- /dev/null +++ b/Api.Authorization/Api.Authorization.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-jwt-secret.yaml = .kubernetes\auth-jwt-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Authorization.Models", "Api.Authorization.Models\Api.Authorization.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Authorization", "Api.Authorization\Api.Authorization.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Authorization", ".tests\Tests.Api.Authorization\Tests.Api.Authorization.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Authorization/Api.Authorization/Api.Authorization.csproj b/Api.Authorization/Api.Authorization/Api.Authorization.csproj new file mode 100644 index 00000000..eb98bdda --- /dev/null +++ b/Api.Authorization/Api.Authorization/Api.Authorization.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Controllers/AuthController.cs b/Api.Authorization/Api.Authorization/Controllers/AuthController.cs new file mode 100644 index 00000000..612d7030 --- /dev/null +++ b/Api.Authorization/Api.Authorization/Controllers/AuthController.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Api.Mvc.Authentication.Abstractions; + +namespace Api.Authorization.Controllers; + +/// +public class AuthController(ILogger logger, IAuthRepository authRepository) + : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Controllers/ExamplesController.cs b/Api.Authorization/Api.Authorization/Controllers/ExamplesController.cs new file mode 100644 index 00000000..9c055a05 --- /dev/null +++ b/Api.Authorization/Api.Authorization/Controllers/ExamplesController.cs @@ -0,0 +1,49 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Authorization.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Authenticated Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("authenticated")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task AuthenticatedAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("authenticated"); + } + + /// + /// Forbidden Action. + /// + /// The cancellation token. + /// A message. + /// Unauthorized. + [HttpGet] + [Route("forbidden")] + [Authorize(Policy = "CustomPolicy")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task ForbiddenAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("forbidden"); + } +} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Dockerfile.Local b/Api.Authorization/Api.Authorization/Dockerfile.Local new file mode 100644 index 00000000..bd2f4fec --- /dev/null +++ b/Api.Authorization/Api.Authorization/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Authorization.dll"] \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Extensions/ServiceCollectionExtensions.cs b/Api.Authorization/Api.Authorization/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 00000000..9bb73e85 --- /dev/null +++ b/Api.Authorization/Api.Authorization/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.Extensions.DependencyInjection; + +namespace Api.Authorization.Extensions; + +internal static class ServiceCollectionExtensions +{ + internal static IServiceCollection AddCustomAuthorizationPolicy(this IServiceCollection services) + { + ArgumentNullException.ThrowIfNull(services); + + services + .AddAuthorization(x => + { + x.AddPolicy("CustomPolicy", y => y.RequireClaim("CustomClaim")); + }); + + return services; + } +} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/Program.cs b/Api.Authorization/Api.Authorization/Program.cs new file mode 100644 index 00000000..6a29bda3 --- /dev/null +++ b/Api.Authorization/Api.Authorization/Program.cs @@ -0,0 +1,11 @@ +using Api.Authorization.Extensions; +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddCustomAuthorizationPolicy(); + }) + .Build() + .Run(); diff --git a/Api.Authorization/Api.Authorization/Properties/InternalsVisibleTo.cs b/Api.Authorization/Api.Authorization/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..49bb94fb --- /dev/null +++ b/Api.Authorization/Api.Authorization/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Authorization")] \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/appsettings.Development.json b/Api.Authorization/Api.Authorization/appsettings.Development.json new file mode 100644 index 00000000..3ecf604f --- /dev/null +++ b/Api.Authorization/Api.Authorization/appsettings.Development.json @@ -0,0 +1,26 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Authentication": { + "Jwt": { + "Issuer": "Development.nano", + "Audience": "Development.nano", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", + "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV", + "Expiration": "24:00:00", + "RootLogin": { + "Username": "admin@domain.com", + "Password": "abc12|+d34DadD" + } + } + } + } +} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/appsettings.Production.json b/Api.Authorization/Api.Authorization/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Authorization/Api.Authorization/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/appsettings.Staging.json b/Api.Authorization/Api.Authorization/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Authorization/Api.Authorization/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Authorization/Api.Authorization/appsettings.json b/Api.Authorization/Api.Authorization/appsettings.json new file mode 100644 index 00000000..1af6db64 --- /dev/null +++ b/Api.Authorization/Api.Authorization/appsettings.json @@ -0,0 +1,25 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Documentation": { + }, + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + } + } +} \ No newline at end of file diff --git a/Api.Authorization/Dockerfile b/Api.Authorization/Dockerfile new file mode 100644 index 00000000..3a70d191 --- /dev/null +++ b/Api.Authorization/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Authorization.dll"] \ No newline at end of file diff --git a/Api.Authorization/LICENSE b/Api.Authorization/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Authorization/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Authorization/README.md b/Api.Authorization/README.md new file mode 100644 index 00000000..a8c62533 --- /dev/null +++ b/Api.Authorization/README.md @@ -0,0 +1,46 @@ +# Api.Authorization + +> _Nano API application with custom authorization policy._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Auth.RootLogin](https://github.com/Nano-Core/Nano.Lessons/tree/master/Auth.RootLogin)**. + +A custom authorization policy has been configured for the application. The `ServiceCollectionExtensions.AddCustomAuthorizationPolicy(...)` method adds the `CustomPolicy`, +and the `/forbidden` endpoint enforces the policy using a custom `[Authorize(Policy = "CustomPolicy")]` annotation. The policy itself requires the JWT token to contain +a `CustomClaim` claim type. + +API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration +are automatically excluded. In this example, only the root login action is exposed. + +API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration +are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +The following endpoint from the auth controller is available for testing. + +| Endpoint | Description | +| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/auth/login/root` | Logins with the root credentials from configuration, and returns a simple `200 OK` response with a JWT token. | + +Additionally, the following endpoint is available for testing authorization. + +| Endpoint | Description | +| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/authenticate` | Returns a simple `200 OK` response, when JWT authorization is successful. | +| `http://localhost:8080/api/examples/forbidden` | Returns a simple `200 OK` response, when JWT authorization is successful with `CustomClaim`, otherwise returns `403 FORBIDDEN` response. | + +> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#authentication)**. diff --git a/Api.Authorization/icon.png b/Api.Authorization/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.ContentNegotiation/.docker/docker-compose.yml b/Api.ContentNegotiation/.docker/docker-compose.yml new file mode 100644 index 00000000..31e6a9a3 --- /dev/null +++ b/Api.ContentNegotiation/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.contentnegotiation: + image: api.contentnegotiation + hostname: api-contentnegotiation + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.ContentNegotiation + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.ContentNegotiation/.dockerignore b/Api.ContentNegotiation/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.ContentNegotiation/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.ContentNegotiation/.github/config/slack.yml b/Api.ContentNegotiation/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.ContentNegotiation/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml b/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..2b200b6d --- /dev/null +++ b/Api.ContentNegotiation/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.ContentNegotiation + IMAGE_NAME: api.contentnegotiation + SERVICE_NAME: api-contentnegotiation + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ContentNegotiation/.gitignore b/Api.ContentNegotiation/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.ContentNegotiation/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ContentNegotiation/.kubernetes/autoscaler.yaml b/Api.ContentNegotiation/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.ContentNegotiation/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ContentNegotiation/.kubernetes/configmap.yaml b/Api.ContentNegotiation/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.ContentNegotiation/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ContentNegotiation/.kubernetes/deployment.yaml b/Api.ContentNegotiation/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.ContentNegotiation/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.ContentNegotiation/.kubernetes/service.yaml b/Api.ContentNegotiation/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.ContentNegotiation/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Properties/DoNotParallelize.cs b/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Tests.Api.ContentNegotiation.csproj b/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Tests.Api.ContentNegotiation.csproj new file mode 100644 index 00000000..101e0cd7 --- /dev/null +++ b/Api.ContentNegotiation/.tests/Tests.Api.ContentNegotiation/Tests.Api.ContentNegotiation.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj b/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation.Models/Api.ContentNegotiation.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation.sln b/Api.ContentNegotiation/Api.ContentNegotiation.sln new file mode 100644 index 00000000..611424a3 --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ContentNegotiation.Models", "Api.ContentNegotiation.Models\Api.ContentNegotiation.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ContentNegotiation", "Api.ContentNegotiation\Api.ContentNegotiation.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ContentNegotiation", ".tests\Tests.Api.ContentNegotiation\Tests.Api.ContentNegotiation.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Api.ContentNegotiation.csproj b/Api.ContentNegotiation/Api.ContentNegotiation/Api.ContentNegotiation.csproj new file mode 100644 index 00000000..99b477a7 --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/Api.ContentNegotiation.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Controllers/ExamplesController.cs b/Api.ContentNegotiation/Api.ContentNegotiation/Controllers/ExamplesController.cs new file mode 100644 index 00000000..c34fc3e4 --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.ContentNegotiation.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Content Negotiation Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("content-negotiation")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task ContentNegotiationAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("content-negotiation"); + } +} \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local b/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local new file mode 100644 index 00000000..dc9daf88 --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ContentNegotiation.dll"] \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Program.cs b/Api.ContentNegotiation/Api.ContentNegotiation/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/Properties/InternalsVisibleTo.cs b/Api.ContentNegotiation/Api.ContentNegotiation/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..27e3628a --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ContentNegotiation")] \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Development.json b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Production.json b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Staging.json b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.json b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.ContentNegotiation/Api.ContentNegotiation/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.ContentNegotiation/Dockerfile b/Api.ContentNegotiation/Dockerfile new file mode 100644 index 00000000..658250a4 --- /dev/null +++ b/Api.ContentNegotiation/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.ContentNegotiation.dll"] \ No newline at end of file diff --git a/Api.ContentNegotiation/LICENSE b/Api.ContentNegotiation/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.ContentNegotiation/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.ContentNegotiation/README.md b/Api.ContentNegotiation/README.md new file mode 100644 index 00000000..9aeb4898 --- /dev/null +++ b/Api.ContentNegotiation/README.md @@ -0,0 +1,30 @@ +# Api.ContentNegotiation + +> _Nano API application with content negotiation._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example demonstrates how Nano handles a missing `Accept` header by defaulting the response format to JSON. + +The following endpoint is available for testing: + +| Endpoint | Description | +| --------------------------------------------------------- | --------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/content-negotiation` | Returns a simple `200 OK` response, no matter if `Accept` header is set or not. | + +> 📖 Learn more about **[Nano Content Negotiation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#content-negotiation)**. diff --git a/Api.ContentNegotiation/icon.png b/Api.ContentNegotiation/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Cookies/.docker/docker-compose.yml b/Api.Cookies/.docker/docker-compose.yml new file mode 100644 index 00000000..2ad288b3 --- /dev/null +++ b/Api.Cookies/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.cookies: + image: api.cookies + hostname: api-cookies + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Cookies + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Cookies/.dockerignore b/Api.Cookies/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Cookies/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Cookies/.github/config/slack.yml b/Api.Cookies/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Cookies/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Cookies/.github/workflows/build-and-deploy.yml b/Api.Cookies/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..3a341c47 --- /dev/null +++ b/Api.Cookies/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Cookies + IMAGE_NAME: api.cookies + SERVICE_NAME: api-cookies + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Cookies/.gitignore b/Api.Cookies/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Cookies/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Cookies/.kubernetes/autoscaler.yaml b/Api.Cookies/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Cookies/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Cookies/.kubernetes/configmap.yaml b/Api.Cookies/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Cookies/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Cookies/.kubernetes/deployment.yaml b/Api.Cookies/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Cookies/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Cookies/.kubernetes/service.yaml b/Api.Cookies/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Cookies/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Cookies/.tests/Tests.Api.Cookies/Properties/DoNotParallelize.cs b/Api.Cookies/.tests/Tests.Api.Cookies/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Cookies/.tests/Tests.Api.Cookies/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Cookies/.tests/Tests.Api.Cookies/Tests.Api.Cookies.csproj b/Api.Cookies/.tests/Tests.Api.Cookies/Tests.Api.Cookies.csproj new file mode 100644 index 00000000..1cddb1ef --- /dev/null +++ b/Api.Cookies/.tests/Tests.Api.Cookies/Tests.Api.Cookies.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj b/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Cookies/Api.Cookies.Models/Api.Cookies.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies.sln b/Api.Cookies/Api.Cookies.sln new file mode 100644 index 00000000..4ed41ddd --- /dev/null +++ b/Api.Cookies/Api.Cookies.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Cookies.Models", "Api.Cookies.Models\Api.Cookies.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Cookies", "Api.Cookies\Api.Cookies.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Cookies", ".tests\Tests.Api.Cookies\Tests.Api.Cookies.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Cookies/Api.Cookies/Api.Cookies.csproj b/Api.Cookies/Api.Cookies/Api.Cookies.csproj new file mode 100644 index 00000000..6ac27bd3 --- /dev/null +++ b/Api.Cookies/Api.Cookies/Api.Cookies.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/Controllers/ExamplesController.cs b/Api.Cookies/Api.Cookies/Controllers/ExamplesController.cs new file mode 100644 index 00000000..804fa030 --- /dev/null +++ b/Api.Cookies/Api.Cookies/Controllers/ExamplesController.cs @@ -0,0 +1,91 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Cookies.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + private const string COOKIE_KEY = "TestKey"; + + /// + /// Set Cookie Action. + /// + /// The value to set in cookie. + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("set-cookie")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task SetCookieAsync([FromQuery][Required] string value, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + var options = new CookieOptions + { + HttpOnly = false, + Secure = true, + SameSite = SameSiteMode.Strict, + Expires = DateTimeOffset.UtcNow.AddMinutes(30) + }; + + this.Response.Cookies + .Append(COOKIE_KEY, value, options); + + return this.Ok("Cookie set"); + } + + /// + /// Get Cookie Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("get-cookie")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task GetCookie(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + var success = this.Request.Cookies + .TryGetValue(COOKIE_KEY, out var value); + + if (success) + { + return Ok($"Cookie value: {value}"); + } + + return Ok("cookie is empty or expired"); + } + + /// + /// Delete Cookie Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("delete-cookie")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task DeleteCookie(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + this.Response.Cookies + .Delete(COOKIE_KEY); + + return Ok("Cookie deleted"); + } +} \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/Dockerfile.Local b/Api.Cookies/Api.Cookies/Dockerfile.Local new file mode 100644 index 00000000..09cc9dd0 --- /dev/null +++ b/Api.Cookies/Api.Cookies/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Cookies.dll"] \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/Program.cs b/Api.Cookies/Api.Cookies/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Cookies/Api.Cookies/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Cookies/Api.Cookies/Properties/InternalsVisibleTo.cs b/Api.Cookies/Api.Cookies/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..fbbf81d6 --- /dev/null +++ b/Api.Cookies/Api.Cookies/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Cookies")] \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/appsettings.Development.json b/Api.Cookies/Api.Cookies/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Cookies/Api.Cookies/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/appsettings.Production.json b/Api.Cookies/Api.Cookies/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Cookies/Api.Cookies/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/appsettings.Staging.json b/Api.Cookies/Api.Cookies/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Cookies/Api.Cookies/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Cookies/Api.Cookies/appsettings.json b/Api.Cookies/Api.Cookies/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.Cookies/Api.Cookies/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.Cookies/Dockerfile b/Api.Cookies/Dockerfile new file mode 100644 index 00000000..2f4f37d9 --- /dev/null +++ b/Api.Cookies/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Cookies.dll"] \ No newline at end of file diff --git a/Api.Cookies/LICENSE b/Api.Cookies/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Cookies/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Cookies/README.md b/Api.Cookies/README.md new file mode 100644 index 00000000..b2787c1b --- /dev/null +++ b/Api.Cookies/README.md @@ -0,0 +1,33 @@ +# Api.Cookies + +> _Nano API application with cookies._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example demonstrates the use of cookies in a Nano application. + +The following endpoint is available for testing: + +| Endpoint | Description | +| -------------------------------------------------- | --------------------------------------------- | +| `http://localhost:8080/api/examples/set-cookie` | Sets a cookie returns a `200 OK`. | +| `http://localhost:8080/api/examples/get-cookie` | Gets a cookie if set and returns a `200 OK`. | +| `http://localhost:8080/api/examples/delete-cookie` | Deletes the cookie and returns a `200 OK`. | + +> 📖 Learn more about **[Nano Cookies](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Apid#cookies)**. + diff --git a/Api.Cookies/icon.png b/Api.Cookies/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.CustomConfigSection/.docker/docker-compose.yml b/Api.CustomConfigSection/.docker/docker-compose.yml new file mode 100644 index 00000000..9c4a6198 --- /dev/null +++ b/Api.CustomConfigSection/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.customconfigsection: + image: api.customconfigsection + hostname: api-customconfigsection + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.CustomConfigSection + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.CustomConfigSection/.dockerignore b/Api.CustomConfigSection/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.CustomConfigSection/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.CustomConfigSection/.github/config/slack.yml b/Api.CustomConfigSection/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.CustomConfigSection/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml b/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..add53801 --- /dev/null +++ b/Api.CustomConfigSection/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.CustomConfigSection + IMAGE_NAME: api.customconfigsection + SERVICE_NAME: api-customconfigsection + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.CustomConfigSection/.gitignore b/Api.CustomConfigSection/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.CustomConfigSection/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.CustomConfigSection/.kubernetes/autoscaler.yaml b/Api.CustomConfigSection/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.CustomConfigSection/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.CustomConfigSection/.kubernetes/configmap.yaml b/Api.CustomConfigSection/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.CustomConfigSection/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.CustomConfigSection/.kubernetes/deployment.yaml b/Api.CustomConfigSection/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.CustomConfigSection/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.CustomConfigSection/.kubernetes/service.yaml b/Api.CustomConfigSection/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.CustomConfigSection/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Properties/DoNotParallelize.cs b/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Tests.Api.CustomConfigSection.csproj b/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Tests.Api.CustomConfigSection.csproj new file mode 100644 index 00000000..1174e01e --- /dev/null +++ b/Api.CustomConfigSection/.tests/Tests.Api.CustomConfigSection/Tests.Api.CustomConfigSection.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj b/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection.Models/Api.CustomConfigSection.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection.sln b/Api.CustomConfigSection/Api.CustomConfigSection.sln new file mode 100644 index 00000000..05a5c0e0 --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomConfigSection.Models", "Api.CustomConfigSection.Models\Api.CustomConfigSection.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomConfigSection", "Api.CustomConfigSection\Api.CustomConfigSection.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.CustomConfigSection", ".tests\Tests.Api.CustomConfigSection\Tests.Api.CustomConfigSection.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Api.CustomConfigSection.csproj b/Api.CustomConfigSection/Api.CustomConfigSection/Api.CustomConfigSection.csproj new file mode 100644 index 00000000..2863f706 --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/Api.CustomConfigSection.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Config/CustomOptions.cs b/Api.CustomConfigSection/Api.CustomConfigSection/Config/CustomOptions.cs new file mode 100644 index 00000000..1321de75 --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/Config/CustomOptions.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Api.CustomConfigSection.Config; + +/// +/// Custom Options. +/// +public class CustomOptions +{ + internal static string SectionName => "Custom"; + + /// + /// Value. + /// + [Required] + public virtual string Value { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Controllers/ExamplesController.cs b/Api.CustomConfigSection/Api.CustomConfigSection/Controllers/ExamplesController.cs new file mode 100644 index 00000000..4c465b2e --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/Controllers/ExamplesController.cs @@ -0,0 +1,37 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.CustomConfigSection.Config; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Nano.App.Api.Controllers; + +namespace Api.CustomConfigSection.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IOptionsMonitor customOptions) : BaseController(logger) +{ + private readonly IOptionsMonitor customOptions = customOptions ?? throw new ArgumentNullException(nameof(customOptions)); + + /// + /// Custom Config Section Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("custom-config-section")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task CustomConfigSectionAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok($"Custom Config Section Value: '{this.customOptions.CurrentValue.Value}'"); + } +} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local b/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local new file mode 100644 index 00000000..96d8dc2e --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Program.cs b/Api.CustomConfigSection/Api.CustomConfigSection/Program.cs new file mode 100644 index 00000000..eb79f126 --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/Program.cs @@ -0,0 +1,12 @@ +using Api.CustomConfigSection.Config; +using Nano.App.Api; +using Nano.Common.Config.Extensions; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoConfigSection(CustomOptions.SectionName, out _); + }) + .Build() + .Run(); diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/Properties/InternalsVisibleTo.cs b/Api.CustomConfigSection/Api.CustomConfigSection/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..a6d1e8ad --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.CustomConfigSection")] \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Development.json b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Production.json b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Staging.json b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.json b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.json new file mode 100644 index 00000000..33396aac --- /dev/null +++ b/Api.CustomConfigSection/Api.CustomConfigSection/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Custom": { + "Value": "custom-value" + } +} \ No newline at end of file diff --git a/Api.CustomConfigSection/Dockerfile b/Api.CustomConfigSection/Dockerfile new file mode 100644 index 00000000..ee947826 --- /dev/null +++ b/Api.CustomConfigSection/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Api.CustomConfigSection/LICENSE b/Api.CustomConfigSection/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.CustomConfigSection/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.CustomConfigSection/README.md b/Api.CustomConfigSection/README.md new file mode 100644 index 00000000..74dcdc22 --- /dev/null +++ b/Api.CustomConfigSection/README.md @@ -0,0 +1,65 @@ +# Api.CustomConfigSection + +> _Nano API application with a custom configuration section._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Rememmber to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) +* [Registration](#registration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example illustrates how custom configuration sections can be effortlessly registered within a Nano application. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ----------------------------------------------------------- | -------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/custom-config-section` | Returns a simple `200 OK` response with the custom configuration value. | + +> 📖 Learn more about **[Nano Custom Configuration Sections](https://github.com/Nano-Core/Nano.Library/Nano.App.Api/README.md#custom-configuration-section)**. + +## Configuration +A custom configuration section has been added to `appsettings.json`: + +```json +"Custom": { + "Value": "custom-value" +} +``` + +## Registration +An option class matching the structure of the configuration has been implemented. + +```csharp +public class CustomOptions +{ + internal static string SectionName => "Custom"; + + [Required] + public virtual string Value { get; set; } = null!; +} +``` + +Finally, the options model has been registered with the section in startup, as shown below. + +```csharp +... +.ConfigureServices(x => +{ + x.AddNanoConfigSection(CustomOptions.SectionName, out _); +}) +... +``` diff --git a/Api.CustomConfigSection/icon.png b/Api.CustomConfigSection/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.CustomMiddleware/.docker/docker-compose.yml b/Api.CustomMiddleware/.docker/docker-compose.yml new file mode 100644 index 00000000..21e01392 --- /dev/null +++ b/Api.CustomMiddleware/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.custommiddleware: + image: api.custommiddleware + hostname: api-custommiddleware + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.CustomMiddleware + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.CustomMiddleware/.dockerignore b/Api.CustomMiddleware/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.CustomMiddleware/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.CustomMiddleware/.github/config/slack.yml b/Api.CustomMiddleware/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.CustomMiddleware/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml b/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..b1ae72ca --- /dev/null +++ b/Api.CustomMiddleware/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.CustomMiddleware + IMAGE_NAME: api.custommiddleware + SERVICE_NAME: api-custommiddleware + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.CustomMiddleware/.gitignore b/Api.CustomMiddleware/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.CustomMiddleware/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.CustomMiddleware/.kubernetes/autoscaler.yaml b/Api.CustomMiddleware/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.CustomMiddleware/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.CustomMiddleware/.kubernetes/configmap.yaml b/Api.CustomMiddleware/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.CustomMiddleware/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.CustomMiddleware/.kubernetes/deployment.yaml b/Api.CustomMiddleware/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.CustomMiddleware/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.CustomMiddleware/.kubernetes/service.yaml b/Api.CustomMiddleware/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.CustomMiddleware/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Properties/DoNotParallelize.cs b/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Tests.Api.CustomMiddleware.csproj b/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Tests.Api.CustomMiddleware.csproj new file mode 100644 index 00000000..a47d09e0 --- /dev/null +++ b/Api.CustomMiddleware/.tests/Tests.Api.CustomMiddleware/Tests.Api.CustomMiddleware.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj b/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware.Models/Api.CustomMiddleware.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware.sln b/Api.CustomMiddleware/Api.CustomMiddleware.sln new file mode 100644 index 00000000..0cdb2e95 --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomMiddleware.Models", "Api.CustomMiddleware.Models\Api.CustomMiddleware.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomMiddleware", "Api.CustomMiddleware\Api.CustomMiddleware.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.CustomMiddleware", ".tests\Tests.Api.CustomMiddleware\Tests.Api.CustomMiddleware.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Api.CustomMiddleware.csproj b/Api.CustomMiddleware/Api.CustomMiddleware/Api.CustomMiddleware.csproj new file mode 100644 index 00000000..05602204 --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/Api.CustomMiddleware.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Controllers/ExamplesController.cs b/Api.CustomMiddleware/Api.CustomMiddleware/Controllers/ExamplesController.cs new file mode 100644 index 00000000..4f1ebd38 --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.CustomMiddleware.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Custom Middleware Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("custom-middleware")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task CustomMiddlewareAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("custom-middleware"); + } +} \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local b/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local new file mode 100644 index 00000000..e059926f --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.CustomMiddleware.dll"] \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Program.cs b/Api.CustomMiddleware/Api.CustomMiddleware/Program.cs new file mode 100644 index 00000000..e96b39e7 --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/Program.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNetCore.Builder; +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build(x => + { + x.Use((context, next) => + { + context.Response.Headers["CustomMiddleware"] = "awesome"; + + return next(); + }); + }) + .Run(); \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/Properties/InternalsVisibleTo.cs b/Api.CustomMiddleware/Api.CustomMiddleware/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..b62f0409 --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.CustomMiddleware")] \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Development.json b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Production.json b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Staging.json b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.json b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.CustomMiddleware/Api.CustomMiddleware/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.CustomMiddleware/Dockerfile b/Api.CustomMiddleware/Dockerfile new file mode 100644 index 00000000..a5772da5 --- /dev/null +++ b/Api.CustomMiddleware/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.CustomMiddleware.dll"] \ No newline at end of file diff --git a/Api.CustomMiddleware/LICENSE b/Api.CustomMiddleware/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.CustomMiddleware/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.CustomMiddleware/README.md b/Api.CustomMiddleware/README.md new file mode 100644 index 00000000..9a1136fb --- /dev/null +++ b/Api.CustomMiddleware/README.md @@ -0,0 +1,49 @@ +# Api.CustomMiddleware + +> _Nano API application with http._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example illustrates how custom middleware can be registered within a Nano application. + +The following endpoint is available for testing: + +| Endpoint | Description | +| ------------------------------------------------------- | ------------------------------------------------------------ | +| `http://localhost:8080/api/examples/custom-middleware` | Returns a simple `200 OK` response, with the custom header. | + +> 📖 Learn more about **[Nano Custom Middleware](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#custom-middleware)**. + +## Registration +The application register custom middleware that adds a header `CustomMiddleware` to all response with the value `awesome`, as shown below. + +```csharp +... +.Build(builder => +{ + builder + .Use((context, next) => + { + context.Response.Headers["CustomMiddleware"] = "awesome"; + + return next(); + }); +}) +... +``` diff --git a/Api.CustomMiddleware/icon.png b/Api.CustomMiddleware/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.CustomService/.docker/docker-compose.yml b/Api.CustomService/.docker/docker-compose.yml new file mode 100644 index 00000000..2b5bedb8 --- /dev/null +++ b/Api.CustomService/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.customservice: + image: api.customservice + hostname: api-customservice + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.CustomService + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.CustomService/.dockerignore b/Api.CustomService/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.CustomService/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.CustomService/.github/config/slack.yml b/Api.CustomService/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.CustomService/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.CustomService/.github/workflows/build-and-deploy.yml b/Api.CustomService/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..4cb38d5a --- /dev/null +++ b/Api.CustomService/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.CustomService + IMAGE_NAME: api.customservice + SERVICE_NAME: api-customservice + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.CustomService/.gitignore b/Api.CustomService/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.CustomService/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.CustomService/.kubernetes/autoscaler.yaml b/Api.CustomService/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.CustomService/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.CustomService/.kubernetes/configmap.yaml b/Api.CustomService/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.CustomService/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.CustomService/.kubernetes/deployment.yaml b/Api.CustomService/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.CustomService/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.CustomService/.kubernetes/service.yaml b/Api.CustomService/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.CustomService/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.CustomService/.tests/Tests.Api.CustomService/Properties/DoNotParallelize.cs b/Api.CustomService/.tests/Tests.Api.CustomService/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.CustomService/.tests/Tests.Api.CustomService/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.CustomService/.tests/Tests.Api.CustomService/Tests.Api.CustomService.csproj b/Api.CustomService/.tests/Tests.Api.CustomService/Tests.Api.CustomService.csproj new file mode 100644 index 00000000..2366fb2e --- /dev/null +++ b/Api.CustomService/.tests/Tests.Api.CustomService/Tests.Api.CustomService.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj b/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.CustomService/Api.CustomService.Models/Api.CustomService.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService.sln b/Api.CustomService/Api.CustomService.sln new file mode 100644 index 00000000..18b03228 --- /dev/null +++ b/Api.CustomService/Api.CustomService.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomService.Models", "Api.CustomService.Models\Api.CustomService.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.CustomService", "Api.CustomService\Api.CustomService.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.CustomService", ".tests\Tests.Api.CustomService\Tests.Api.CustomService.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.CustomService/Api.CustomService/Api.CustomService.csproj b/Api.CustomService/Api.CustomService/Api.CustomService.csproj new file mode 100644 index 00000000..25b46e86 --- /dev/null +++ b/Api.CustomService/Api.CustomService/Api.CustomService.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Controllers/ExamplesController.cs b/Api.CustomService/Api.CustomService/Controllers/ExamplesController.cs new file mode 100644 index 00000000..8497b956 --- /dev/null +++ b/Api.CustomService/Api.CustomService/Controllers/ExamplesController.cs @@ -0,0 +1,37 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.CustomService.Services.Abstractions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.CustomService.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IExampleService exampleService) : BaseController(logger) +{ + private readonly IExampleService exampleService = exampleService ?? throw new ArgumentNullException(nameof(exampleService)); + + /// + /// Custom Service Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("custom-service")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task CustomServiceAsync(CancellationToken cancellationToken = default) + { + var message = await this.exampleService + .GetMessage(); + + return this.Ok($"custom-service message: {message}"); + } +} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Dockerfile.Local b/Api.CustomService/Api.CustomService/Dockerfile.Local new file mode 100644 index 00000000..83ac0c36 --- /dev/null +++ b/Api.CustomService/Api.CustomService/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.CustomService.dll"] \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Program.cs b/Api.CustomService/Api.CustomService/Program.cs new file mode 100644 index 00000000..07917592 --- /dev/null +++ b/Api.CustomService/Api.CustomService/Program.cs @@ -0,0 +1,13 @@ +using Api.CustomService.Services; +using Api.CustomService.Services.Abstractions; +using Microsoft.Extensions.DependencyInjection; +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddScoped(); + }) + .Build() + .Run(); diff --git a/Api.CustomService/Api.CustomService/Properties/InternalsVisibleTo.cs b/Api.CustomService/Api.CustomService/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..c8d30be6 --- /dev/null +++ b/Api.CustomService/Api.CustomService/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.CustomService")] \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Services/Abstractions/IExampleService.cs b/Api.CustomService/Api.CustomService/Services/Abstractions/IExampleService.cs new file mode 100644 index 00000000..1413cf76 --- /dev/null +++ b/Api.CustomService/Api.CustomService/Services/Abstractions/IExampleService.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace Api.CustomService.Services.Abstractions; + +/// +/// Example Service Interface. +/// +public interface IExampleService +{ + /// + /// Get Message. + /// + /// A message. + Task GetMessage(); +} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/Services/ExampleService.cs b/Api.CustomService/Api.CustomService/Services/ExampleService.cs new file mode 100644 index 00000000..a45d5c6d --- /dev/null +++ b/Api.CustomService/Api.CustomService/Services/ExampleService.cs @@ -0,0 +1,16 @@ +using Api.CustomService.Services.Abstractions; +using System.Threading.Tasks; + +namespace Api.CustomService.Services; + +/// +/// Example Service. +/// +public class ExampleService : IExampleService +{ + /// + public Task GetMessage() + { + return Task.FromResult("Message from example service."); + } +} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/appsettings.Development.json b/Api.CustomService/Api.CustomService/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomService/Api.CustomService/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/appsettings.Production.json b/Api.CustomService/Api.CustomService/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomService/Api.CustomService/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/appsettings.Staging.json b/Api.CustomService/Api.CustomService/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.CustomService/Api.CustomService/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.CustomService/Api.CustomService/appsettings.json b/Api.CustomService/Api.CustomService/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.CustomService/Api.CustomService/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.CustomService/Dockerfile b/Api.CustomService/Dockerfile new file mode 100644 index 00000000..8679c6c7 --- /dev/null +++ b/Api.CustomService/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.CustomService.dll"] \ No newline at end of file diff --git a/Api.CustomService/LICENSE b/Api.CustomService/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.CustomService/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.CustomService/README.md b/Api.CustomService/README.md new file mode 100644 index 00000000..7c6a3071 --- /dev/null +++ b/Api.CustomService/README.md @@ -0,0 +1,44 @@ +# Api.CustomService + +> _Nano API application with custom scoped service._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example demonstrates how a custom service implementation can be registered and used within a Nano application. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ---------------------------------------------------- | -------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/custom-servuce` | Returns a simple `200 OK` response, with a message from the `IExampleServuce` | + +> 📖 Learn more about **[Nano Custom Services](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App/README.md#custom-services)**. + +## Registration +A custom service, `IExampleService` has been added and implemented. In `program.cs` the service is registered using `ConfigureService(...)` method as shown below + +```csharp +... +.ConfigureServices(services => +{ + services + .AddScoped(); +}) +... +``` diff --git a/Api.CustomService/icon.png b/Api.CustomService/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.Audit/.docker/docker-compose.yml b/Api.Data.Audit/.docker/docker-compose.yml new file mode 100644 index 00000000..ee96146f --- /dev/null +++ b/Api.Data.Audit/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.repository.audit: + image: api.data.repository.audit + hostname: api-data-audit + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.Audit + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.Audit/.dockerignore b/Api.Data.Audit/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.Audit/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.Audit/.github/config/slack.yml b/Api.Data.Audit/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.Audit/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Audit/.github/workflows/build-and-deploy.yml b/Api.Data.Audit/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..e8e13f4a --- /dev/null +++ b/Api.Data.Audit/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.Audit + IMAGE_NAME: api.data.audit + SERVICE_NAME: api-data-audit + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Audit/.gitignore b/Api.Data.Audit/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.Audit/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Audit/.kubernetes/auth-sql-secret.yaml b/Api.Data.Audit/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.Audit/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.Audit/.kubernetes/autoscaler.yaml b/Api.Data.Audit/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.Audit/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Audit/.kubernetes/configmap.yaml b/Api.Data.Audit/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.Audit/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Audit/.kubernetes/deployment.yaml b/Api.Data.Audit/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.Audit/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.Audit/.kubernetes/service.yaml b/Api.Data.Audit/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.Audit/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Properties/DoNotParallelize.cs b/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Tests.Api.Data.Audit.csproj b/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Tests.Api.Data.Audit.csproj new file mode 100644 index 00000000..48e9ce75 --- /dev/null +++ b/Api.Data.Audit/.tests/Tests.Api.Data.Audit/Tests.Api.Data.Audit.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj b/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit.Models/Api.Data.Audit.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.Audit/Api.Data.Audit.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..b98f0e56 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.Audit.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.Models/Example.cs b/Api.Data.Audit/Api.Data.Audit.Models/Example.cs new file mode 100644 index 00000000..349b116e --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit.Models/Example.cs @@ -0,0 +1,28 @@ +using System; +using Nano.Data.Abstractions.Annotations; +using Nano.Data.Abstractions.Models; +using Nano.Data.Abstractions.Models.Abstractions; + +namespace Api.Data.Audit.Models; + +/// +/// Example. +/// +public class Example : BaseEntity, IEntityAuditable, IEntitySoftDeletable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; + + /// + /// Navigation Id. + /// + public virtual Guid? NavigationId { get; set; } + + /// + /// Navigation. + /// + [Include] + public virtual ExampleNavigation? Navigation { get; set; } +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.Models/ExampleNavigation.cs b/Api.Data.Audit/Api.Data.Audit.Models/ExampleNavigation.cs new file mode 100644 index 00000000..021c4740 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit.Models/ExampleNavigation.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using Nano.Data.Abstractions.Models; +using Nano.Data.Abstractions.Models.Abstractions; + +namespace Api.Data.Audit.Models; + +/// +/// Example Navigation. +/// +public class ExampleNavigation : BaseEntity, IEntityAuditable, IEntitySoftDeletable +{ + /// + /// Navigation Name. + /// + public virtual string NavigationName { get; set; } = null!; + + /// + /// Examples. + /// + public virtual ICollection Examples { get; set; } = []; +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.Models/ExampleNoAudit.cs b/Api.Data.Audit/Api.Data.Audit.Models/ExampleNoAudit.cs new file mode 100644 index 00000000..052ee8df --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit.Models/ExampleNoAudit.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Audit.Models; + +/// +/// Example. +/// +public class ExampleNoAudit : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit.sln b/Api.Data.Audit/Api.Data.Audit.sln new file mode 100644 index 00000000..281a45e0 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + .kubernetes\sql-auth-secret.yaml = .kubernetes\sql-auth-secret.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Audit.Models", "Api.Data.Audit.Models\Api.Data.Audit.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Audit", "Api.Data.Audit\Api.Data.Audit.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Audit", ".tests\Tests.Api.Data.Audit\Tests.Api.Data.Audit.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.Audit/Api.Data.Audit/Api.Data.Audit.csproj b/Api.Data.Audit/Api.Data.Audit/Api.Data.Audit.csproj new file mode 100644 index 00000000..644eec10 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Api.Data.Audit.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Controllers/AuditController.cs b/Api.Data.Audit/Api.Data.Audit/Controllers/AuditController.cs new file mode 100644 index 00000000..131c3a1d --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Controllers/AuditController.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.Audit.Controllers; + +/// +public class AuditController(ILogger logger, IRepository repository) + : BaseAuditController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Controllers/ExampleNoAuditsController.cs b/Api.Data.Audit/Api.Data.Audit/Controllers/ExampleNoAuditsController.cs new file mode 100644 index 00000000..40f20b8f --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Controllers/ExampleNoAuditsController.cs @@ -0,0 +1,15 @@ +using Api.Data.Audit.Models; +using Api.Data.Audit.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.Audit.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleNoAuditsController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Controllers/ExamplesController.cs b/Api.Data.Audit/Api.Data.Audit/Controllers/ExamplesController.cs new file mode 100644 index 00000000..888ff808 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.Audit.Models; +using Api.Data.Audit.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.Audit.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleMapping.cs b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..8ad77cee --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,34 @@ +using System; +using Api.Data.Audit.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using Nano.Data.Mappings.Extensions; + +namespace Api.Data.Audit.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasOne(x => x.Navigation) + .WithMany(x => x.Examples); + + builder + .OnUpdating(x => + { + x.Entity.Name += "-triggered"; + }); + } +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNavigationMapping.cs b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNavigationMapping.cs new file mode 100644 index 00000000..642f2122 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNavigationMapping.cs @@ -0,0 +1,27 @@ +using System; +using Api.Data.Audit.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.Audit.Data.Mappings; + +/// +/// Example Navigation Mapping. +/// +public class ExampleNavigationMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.NavigationName); + + builder + .HasMany(x => x.Examples) + .WithOne(x => x.Navigation); + } +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNoAuditMapping.cs b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNoAuditMapping.cs new file mode 100644 index 00000000..54ad4ad7 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Data/Mappings/ExampleNoAuditMapping.cs @@ -0,0 +1,23 @@ +using System; +using Api.Data.Audit.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.Audit.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleNoAuditMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContext.cs b/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContext.cs new file mode 100644 index 00000000..1781e44e --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.Audit.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContextFactory.cs b/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..aeb1d1ec --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.Audit.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local b/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local new file mode 100644 index 00000000..02405ae6 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.Audit.dll"] \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.Designer.cs b/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.Designer.cs new file mode 100644 index 00000000..52f7cfd4 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.Designer.cs @@ -0,0 +1,728 @@ +// +using System; +using Api.Data.Audit.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Audit.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415133755_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Audit.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NavigationId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("NavigationId"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("NavigationName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNavigation"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNoAudit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNoAudit"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.Example", b => + { + b.HasOne("Api.Data.Audit.Models.ExampleNavigation", "Navigation") + .WithMany("Examples") + .HasForeignKey("NavigationId"); + + b.Navigation("Navigation"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => + { + b.Navigation("Examples"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.cs b/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.cs new file mode 100644 index 00000000..dade123c --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Migrations/20260415133755_Initial.cs @@ -0,0 +1,667 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Audit.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleNavigation", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NavigationName = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleNavigation", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleNoAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleNoAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + NavigationId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + table.ForeignKey( + name: "FK_Example_ExampleNavigation_NavigationId", + column: x => x.NavigationId, + principalTable: "ExampleNavigation", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_NavigationId", + table: "Example", + column: "NavigationId"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNavigation_CreatedAt", + table: "ExampleNavigation", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNavigation_IsDeleted", + table: "ExampleNavigation", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNoAudit_CreatedAt", + table: "ExampleNoAudit", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNoAudit_IsDeleted", + table: "ExampleNoAudit", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "ExampleNoAudit"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "ExampleNavigation"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.Audit/Api.Data.Audit/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Audit/Api.Data.Audit/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..1cdb7577 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,725 @@ +// +using System; +using Api.Data.Audit.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Audit.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Audit.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NavigationId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("NavigationId"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("NavigationName") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNavigation"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNoAudit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNoAudit"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.Example", b => + { + b.HasOne("Api.Data.Audit.Models.ExampleNavigation", "Navigation") + .WithMany("Examples") + .HasForeignKey("NavigationId"); + + b.Navigation("Navigation"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.Audit.Models.ExampleNavigation", b => + { + b.Navigation("Examples"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Audit/Api.Data.Audit/Program.cs b/Api.Data.Audit/Api.Data.Audit/Program.cs new file mode 100644 index 00000000..9bf00ea0 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.Audit.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.Audit/Api.Data.Audit/Properties/InternalsVisibleTo.cs b/Api.Data.Audit/Api.Data.Audit/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..44f0ccde --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.Audit")] \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json b/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.Production.json b/Api.Data.Audit/Api.Data.Audit/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.Staging.json b/Api.Data.Audit/Api.Data.Audit/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Audit/Api.Data.Audit/appsettings.json b/Api.Data.Audit/Api.Data.Audit/appsettings.json new file mode 100644 index 00000000..4439ec87 --- /dev/null +++ b/Api.Data.Audit/Api.Data.Audit/appsettings.json @@ -0,0 +1,18 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Documentation": { + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.Audit/Dockerfile b/Api.Data.Audit/Dockerfile new file mode 100644 index 00000000..d8e60daa --- /dev/null +++ b/Api.Data.Audit/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.Audit.dll"] \ No newline at end of file diff --git a/Api.Data.Audit/LICENSE b/Api.Data.Audit/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.Audit/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Audit/README.md b/Api.Data.Audit/README.md new file mode 100644 index 00000000..1f95c3f1 --- /dev/null +++ b/Api.Data.Audit/README.md @@ -0,0 +1,37 @@ +# Api.Data.Audit + +> _Nano API application with data audit logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to +demonstrate audit logging. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. Also an `AuditControlller` derived from +`BaseAuditControlller` has been added. + +The `Example` entity implements `IEntityAuditable`. The entity model is also mapped with a `OnUpdating(...)` trigger to prove that changes to the entity model will be reflected +in audit properties. Last, the entity model also implements the `IEntitySoftDeletable` so when deleted the `AuditEntry.State` will be `SoftDeleted`. + +The `AuditController`, which derives from the base `BaseAuditController` in Nano, exposes read-only endpoints for the `AuditEntry` entity. When retrieving or querying +audit entries, the related `AuditEntryProperties` are automatically included, ensuring that all relevant details are available without additional queries. + +Notice, that when you set the `X-Request-Id` header, the value is automatically recorded in the audit entry. Also the `CreatedBy` is set, and in this case `Anonymous` because +we are invoking the endpoint with an unauthenticated user. + +Also, API documentation has been configured, in order to easier see which audit endpoints are available. It can be accessed +here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +> 📖 Learn more about **[Nano Data Audit](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#audit)**. diff --git a/Api.Data.Audit/icon.png b/Api.Data.Audit/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.EntityEvents/.docker/docker-compose.yml b/Api.Data.EntityEvents/.docker/docker-compose.yml new file mode 100644 index 00000000..c7513f0a --- /dev/null +++ b/Api.Data.EntityEvents/.docker/docker-compose.yml @@ -0,0 +1,62 @@ +services: + api.data.entityevents: + image: api.data.entityevents + hostname: api-data-entityevents + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.EntityEvents + dockerfile: "Dockerfile.Local" + depends_on: + - database + - eventing + networks: + - network + + api.data.entityevents.subscriber: + image: api.data.entityevents.subscriber + hostname: api-data-entityevents.subscriber + restart: on-failure + ports: + - 8181:8181 + build: + context: ../Api.Data.EntityEvents.Subscriber + dockerfile: "Dockerfile.Local" + depends_on: + - database + - eventing + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_ROOT_HOST: '%' + volumes: + - ./init.sql:/docker-entrypoint-initdb.d/init.sql + + eventing: + image: rabbitmq:management + hostname: rabbitmq + ports: + - 5671:5671 + - 5672:5672 + - 15671:15671 + - 15672:15672 + networks: + - network + environment: + RABBITMQ_DEFAULT_USER: rabbitmq_user + RABBITMQ_DEFAULT_PASS: password + RABBITMQ_DEFAULT_VHOST: / + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.EntityEvents/.docker/init.sql b/Api.Data.EntityEvents/.docker/init.sql new file mode 100644 index 00000000..872a2eda --- /dev/null +++ b/Api.Data.EntityEvents/.docker/init.sql @@ -0,0 +1,9 @@ +CREATE DATABASE IF NOT EXISTS nanoDb; +CREATE DATABASE IF NOT EXISTS nanoDb2; + +CREATE USER 'sa'@'%' IDENTIFIED BY 'myPassword_123'; + +GRANT ALL PRIVILEGES ON nanoDb.* TO 'sa'@'%'; +GRANT ALL PRIVILEGES ON nanoDb2.* TO 'sa'@'%'; + +FLUSH PRIVILEGES; \ No newline at end of file diff --git a/Api.Data.EntityEvents/.dockerignore b/Api.Data.EntityEvents/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.EntityEvents/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.EntityEvents/.github/config/slack.yml b/Api.Data.EntityEvents/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.EntityEvents/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml b/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..ab07e819 --- /dev/null +++ b/Api.Data.EntityEvents/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.EntityEvents + IMAGE_NAME: api.data.entityevents + SERVICE_NAME: api-data-entityevents + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.EntityEvents/.gitignore b/Api.Data.EntityEvents/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.EntityEvents/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.EntityEvents/.kubernetes/auth-sql-secret.yaml b/Api.Data.EntityEvents/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.EntityEvents/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.EntityEvents/.kubernetes/autoscaler.yaml b/Api.Data.EntityEvents/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.EntityEvents/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.EntityEvents/.kubernetes/configmap.yaml b/Api.Data.EntityEvents/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.EntityEvents/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.EntityEvents/.kubernetes/deployment.yaml b/Api.Data.EntityEvents/.kubernetes/deployment.yaml new file mode 100644 index 00000000..ddbdae40 --- /dev/null +++ b/Api.Data.EntityEvents/.kubernetes/deployment.yaml @@ -0,0 +1,95 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + - name: Eventing__Credentials__Id + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: username + envFrom: + - name: Eventing__Credentials__Secret + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: password + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.EntityEvents/.kubernetes/service.yaml b/Api.Data.EntityEvents/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.EntityEvents/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Properties/DoNotParallelize.cs b/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Tests.Api.Data.EntityEvents.csproj b/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Tests.Api.Data.EntityEvents.csproj new file mode 100644 index 00000000..8015ac15 --- /dev/null +++ b/Api.Data.EntityEvents/.tests/Tests.Api.Data.EntityEvents/Tests.Api.Data.EntityEvents.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Address.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Address.cs new file mode 100644 index 00000000..584d7745 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Address.cs @@ -0,0 +1,21 @@ +using Nano.Data.Abstractions.Models; +using System.ComponentModel.DataAnnotations; + +namespace Api.Data.EntityEvents.Models; + +/// +/// Address. +/// +public class Address : BaseEntity +{ + /// + /// Street. + /// + [Required] + public virtual string Street { get; set; } = null!; + + /// + /// Profile. + /// + public virtual Profile? Profile { get; set; } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj new file mode 100644 index 00000000..c58bf942 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Api.Data.EntityEvents.Models.csproj @@ -0,0 +1,79 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Criterias/DefaultQueryCriteria.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Criterias/DefaultQueryCriteria.cs new file mode 100644 index 00000000..f73873a8 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Criterias/DefaultQueryCriteria.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.EntityEvents.Models.Criterias; + +/// +public class DefaultQueryCriteria : BaseQueryCriteria +{ + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Customer.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Customer.cs new file mode 100644 index 00000000..50db7b25 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Customer.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Api.Data.EntityEvents.Models.Owned; +using Nano.Data.Abstractions.Annotations; +using Nano.Data.Abstractions.Eventing.Annotations; + +namespace Api.Data.EntityEvents.Models; + +/// +/// Customer. +/// +[Publish( + nameof(Name), + nameof(ProfileId), + $"{nameof(Profile)}.{nameof(Profile.AddressId)}", + $"{nameof(Profile)}.{nameof(Profile.Settings)}.{nameof(ProfileSettings.UseDarkMode)}", + $"{nameof(Profile)}.{nameof(Profile.Address)}.{nameof(Address.Street)}")] +public class Customer : Person +{ + /// + /// Name. + /// + [Required] + public virtual string Name { get; set; } = null!; + + /// + /// Profile Id. + /// + public virtual Guid? ProfileId { get; set; } + + /// + /// Profile. + /// + [Include] + public virtual Profile? Profile { get; set; } + + /// + /// Profile. + /// + [Required] + public virtual ICollection Orders { get; set; } = []; +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Order.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Order.cs new file mode 100644 index 00000000..ac98f255 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Order.cs @@ -0,0 +1,22 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.EntityEvents.Models; + +/// +/// Order. +/// +public class Order : BaseEntity +{ + /// + /// Customer Id. + /// + [Required] + public virtual Guid CustomerId { get; set; } + + /// + /// Customer. + /// + public virtual Customer? Customer { get; set; } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Owned/ProfileSettings.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Owned/ProfileSettings.cs new file mode 100644 index 00000000..63d43f20 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Owned/ProfileSettings.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace Api.Data.EntityEvents.Models.Owned; + +/// +/// Profile Settings +/// +public class ProfileSettings +{ + /// + /// Use Dark Mode. + /// + [Required] + public virtual bool UseDarkMode { get; set; } = false; +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Person.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Person.cs new file mode 100644 index 00000000..924500bc --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Person.cs @@ -0,0 +1,18 @@ +using Nano.Data.Abstractions.Eventing.Annotations; +using Nano.Data.Abstractions.Models; +using System.ComponentModel.DataAnnotations; + +namespace Api.Data.EntityEvents.Models; + +/// +/// Example Parent. +/// +[Publish(nameof(Identitifer))] +public class Person : BaseEntity +{ + /// + /// Name. + /// + [Required] + public virtual string Identitifer { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Profile.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Profile.cs new file mode 100644 index 00000000..7bf7e7c6 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Models/Profile.cs @@ -0,0 +1,35 @@ +using Nano.Data.Abstractions.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Api.Data.EntityEvents.Models.Owned; + +namespace Api.Data.EntityEvents.Models; + +/// +/// Customer Profile. +/// +public class Profile : BaseEntity +{ + /// + /// Address Id. + /// + public virtual Guid? AddressId { get; set; } + + /// + /// Address. + /// + public virtual Address? Address { get; set; } + + /// + /// Address. + /// + [Required] + public virtual ProfileSettings Settings { get; set; } = new(); + + /// + /// Customers. + /// + [Required] + public virtual ICollection Customers { get; set; } = []; +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj new file mode 100644 index 00000000..c58bf942 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Api.Data.EntityEvents.Subscriber.Models.csproj @@ -0,0 +1,79 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Customer.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Customer.cs new file mode 100644 index 00000000..5181e998 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber.Models/Customer.cs @@ -0,0 +1,47 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Eventing.Annotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.EntityEvents.Subscriber.Models; + +/// +/// Customer. +/// +[Subscribe] +public class Customer : BaseEntity +{ + /// + /// Name. + /// + [Required] + public virtual string Identitifer { get; set; } = null!; + + /// + /// Name. + /// + [Required] + public virtual string Name { get; set; } = null!; + + /// + /// Profile Id. + /// + [Required] + public virtual Guid ProfileId { get; set; } + + /// + /// Address Id. + /// + public virtual Guid? AddressId { get; set; } + + /// + /// Use Dark Mode. + /// + [Required] + public virtual bool UseDarkMode { get; set; } = false; + + /// + /// Street. + /// + public virtual string? Street { get; set; } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Api.Data.EntityEvents.Subscriber.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Api.Data.EntityEvents.Subscriber.csproj new file mode 100644 index 00000000..d7d00c69 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Api.Data.EntityEvents.Subscriber.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/Mappings/CustomerMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/Mappings/CustomerMapping.cs new file mode 100644 index 00000000..080eaa31 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/Mappings/CustomerMapping.cs @@ -0,0 +1,41 @@ +using System; +using Api.Data.EntityEvents.Subscriber.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.EntityEvents.Subscriber.Data.Mappings; + +/// +/// Customer Mapping. +/// +public class CustomerMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Identitifer) + .IsRequired(); + + builder + .Property(x => x.Name) + .IsRequired(); + + builder + .Property(x => x.ProfileId); + + builder + .Property(x => x.AddressId); + + builder + .Property(x => x.UseDarkMode) + .IsRequired(); + + builder + .Property(x => x.Street); + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContext.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContext.cs new file mode 100644 index 00000000..f2846958 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.EntityEvents.Subscriber.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContextFactory.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..12b01c22 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.EntityEvents.Subscriber.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local new file mode 100644 index 00000000..6d48bd26 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.EntityEvents.Subscriber.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.Designer.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.Designer.cs new file mode 100644 index 00000000..d74e48dc --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.Designer.cs @@ -0,0 +1,665 @@ +// +using System; +using Api.Data.EntityEvents.Subscriber.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.EntityEvents.Subscriber.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260414100646_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.EntityEvents.Subscriber.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AddressId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Identitifer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("Street") + .HasColumnType("longtext"); + + b.Property("UseDarkMode") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.cs new file mode 100644 index 00000000..b98af064 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/20260414100646_Initial.cs @@ -0,0 +1,603 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.EntityEvents.Subscriber.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Customer", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Identitifer = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProfileId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AddressId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + UseDarkMode = table.Column(type: "tinyint(1)", nullable: false), + Street = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Customer", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Customer_CreatedAt", + table: "Customer", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Customer_IsDeleted", + table: "Customer", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Customer"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..cd1e7608 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,662 @@ +// +using System; +using Api.Data.EntityEvents.Subscriber.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.EntityEvents.Subscriber.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.EntityEvents.Subscriber.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AddressId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Identitifer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.Property("Street") + .HasColumnType("longtext"); + + b.Property("UseDarkMode") + .HasColumnType("tinyint(1)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Program.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Program.cs new file mode 100644 index 00000000..7d99fdf0 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Program.cs @@ -0,0 +1,16 @@ +using Api.Data.EntityEvents.Subscriber.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; +using Nano.Eventing.Extensions; +using Nano.Eventing.RabbitMq; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + x.AddNanoEventing(); + }) + .Build() + .Run(); diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Properties/InternalsVisibleTo.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..a49dcfd1 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.EntityEvents")] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json new file mode 100644 index 00000000..4de4ef5b --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb2;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Production.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Staging.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.json b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.json new file mode 100644 index 00000000..3b1ce56d --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.Subscriber/appsettings.json @@ -0,0 +1,23 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + }, + "Eventing": { + "Host": "rabbitmq", + "Credentials": { + "Id": "rabbitmq_user", + "Secret": "password" + } + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents.sln b/Api.Data.EntityEvents/Api.Data.EntityEvents.sln new file mode 100644 index 00000000..6806eb36 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents.sln @@ -0,0 +1,177 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" + ProjectSection(SolutionItems) = preProject + .docker\init.sql = .docker\init.sql + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.EntityEvents.Models", "Api.Data.EntityEvents.Models\Api.Data.EntityEvents.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.EntityEvents", "Api.Data.EntityEvents\Api.Data.EntityEvents.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.EntityEvents", ".tests\Tests.Api.Data.EntityEvents\Tests.Api.Data.EntityEvents.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing", "..\..\Nano.Library\Nano.Eventing\Nano.Eventing.csproj", "{A8E623BC-70EA-3CC8-AFAD-797F006C4A41}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.RabbitMq", "..\..\Nano.Library\Nano.Eventing.RabbitMq\Nano.Eventing.RabbitMq.csproj", "{0789C863-B371-F968-B9C9-5F3CF3DD9897}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.EntityEvents.Subscriber.Models", "Api.Data.EntityEvents.Subscriber.Models\Api.Data.EntityEvents.Subscriber.Models.csproj", "{129D3824-38AE-3CF2-8038-7180E9F0DA51}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.EntityEvents.Subscriber", "Api.Data.EntityEvents.Subscriber\Api.Data.EntityEvents.Subscriber.csproj", "{EC5580EC-22B1-D1D7-4A59-787F34506C86}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.Build.0 = Release|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.Build.0 = Release|Any CPU + {129D3824-38AE-3CF2-8038-7180E9F0DA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {129D3824-38AE-3CF2-8038-7180E9F0DA51}.Debug|Any CPU.Build.0 = Debug|Any CPU + {129D3824-38AE-3CF2-8038-7180E9F0DA51}.Release|Any CPU.ActiveCfg = Release|Any CPU + {129D3824-38AE-3CF2-8038-7180E9F0DA51}.Release|Any CPU.Build.0 = Release|Any CPU + {EC5580EC-22B1-D1D7-4A59-787F34506C86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC5580EC-22B1-D1D7-4A59-787F34506C86}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC5580EC-22B1-D1D7-4A59-787F34506C86}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC5580EC-22B1-D1D7-4A59-787F34506C86}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {0789C863-B371-F968-B9C9-5F3CF3DD9897} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Api.Data.EntityEvents.csproj b/Api.Data.EntityEvents/Api.Data.EntityEvents/Api.Data.EntityEvents.csproj new file mode 100644 index 00000000..32b1a410 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Api.Data.EntityEvents.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/AddresssController.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/AddresssController.cs new file mode 100644 index 00000000..8863ec75 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/AddresssController.cs @@ -0,0 +1,15 @@ +using Api.Data.EntityEvents.Models; +using Api.Data.EntityEvents.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.EntityEvents.Controllers; + +/// +/// Controller with addresses. +/// +/// The . +/// The . +public class AddresssController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/CustomersController.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/CustomersController.cs new file mode 100644 index 00000000..4abfbf15 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/CustomersController.cs @@ -0,0 +1,15 @@ +using Api.Data.EntityEvents.Models; +using Api.Data.EntityEvents.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.EntityEvents.Controllers; + +/// +/// Controller with customers. +/// +/// The . +/// The . +public class CustomersController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/OrdersController.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/OrdersController.cs new file mode 100644 index 00000000..6c26eb0e --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/OrdersController.cs @@ -0,0 +1,15 @@ +using Api.Data.EntityEvents.Models; +using Api.Data.EntityEvents.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.EntityEvents.Controllers; + +/// +/// Controller with Orders. +/// +/// The . +/// The . +public class OrdersController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/ProfilesController.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/ProfilesController.cs new file mode 100644 index 00000000..52dbcd93 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Controllers/ProfilesController.cs @@ -0,0 +1,15 @@ +using Api.Data.EntityEvents.Models; +using Api.Data.EntityEvents.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.EntityEvents.Controllers; + +/// +/// Controller with profiles. +/// +/// The . +/// The . +public class ProfilesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/AddressMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/AddressMapping.cs new file mode 100644 index 00000000..6c3de7be --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/AddressMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.EntityEvents.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.EntityEvents.Data.Mappings; + +/// +public class AddressMapping : BaseEntityMapping
+{ + /// + public override void Configure(EntityTypeBuilder
builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Street) + .IsRequired(); + + builder + .HasOne(x => x.Profile) + .WithOne(x => x.Address); + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/CustomerMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/CustomerMapping.cs new file mode 100644 index 00000000..43eb1a75 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/CustomerMapping.cs @@ -0,0 +1,48 @@ +using System; +using Api.Data.EntityEvents.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using Nano.Data.Mappings.Extensions; + +namespace Api.Data.EntityEvents.Data.Mappings; + +/// +public class CustomerMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name) + .IsRequired(); + + builder + .HasIndex(x => x.Name); + + builder + .HasOne(x => x.Profile) + .WithMany(x => x.Customers) + .IsRequired(); + + builder + .HasMany(x => x.Orders) + .WithOne(x => x.Customer) + .IsRequired(); + + builder + .OnInserting(x => + { + x.Entity.Name += "-triggred"; + }); + + builder + .OnUpdating(x => + { + x.Entity.Name += "-triggred"; + }); + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/OrderMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/OrderMapping.cs new file mode 100644 index 00000000..17610e87 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/OrderMapping.cs @@ -0,0 +1,23 @@ +using System; +using Api.Data.EntityEvents.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.EntityEvents.Data.Mappings; + +/// +public class OrderMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .HasOne(x => x.Customer) + .WithMany(x => x.Orders) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/PersonMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/PersonMapping.cs new file mode 100644 index 00000000..441a62ab --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/PersonMapping.cs @@ -0,0 +1,23 @@ +using System; +using Api.Data.EntityEvents.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.EntityEvents.Data.Mappings; + +/// +public class PersonMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Identitifer) + .IsRequired() + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/ProfileMapping.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/ProfileMapping.cs new file mode 100644 index 00000000..afd5b7d5 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/Mappings/ProfileMapping.cs @@ -0,0 +1,32 @@ +using System; +using Api.Data.EntityEvents.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.EntityEvents.Data.Mappings; + +/// +public class ProfileMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .HasOne(x => x.Address) + .WithOne(x => x.Profile); + + builder + .HasMany(x => x.Customers) + .WithOne(x => x.Profile) + .IsRequired(); + + builder + .OwnsOne(x => x.Settings) + .Property(x => x.UseDarkMode) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContext.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContext.cs new file mode 100644 index 00000000..3b4e54df --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.EntityEvents.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContextFactory.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..20fc0ad9 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.EntityEvents.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local b/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local new file mode 100644 index 00000000..b78f25ae --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.EntityEvents.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.Designer.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.Designer.cs new file mode 100644 index 00000000..edd93908 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.Designer.cs @@ -0,0 +1,849 @@ +// +using System; +using Api.Data.EntityEvents.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.EntityEvents.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260412140549_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Street") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Address"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Identitifer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.HasIndex("ProfileId"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CustomerId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("CustomerId"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Identitifer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Person"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AddressId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("AddressId") + .IsUnique() + .HasDatabaseName("UX_Profile_AddressId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Profile"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => + { + b.HasOne("Api.Data.EntityEvents.Models.Profile", "Profile") + .WithMany("Customers") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Order", b => + { + b.HasOne("Api.Data.EntityEvents.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => + { + b.HasOne("Api.Data.EntityEvents.Models.Address", "Address") + .WithOne("Profile") + .HasForeignKey("Api.Data.EntityEvents.Models.Profile", "AddressId"); + + b.OwnsOne("Api.Data.EntityEvents.Models.Owned.ProfileSettings", "Settings", b1 => + { + b1.Property("ProfileId") + .HasColumnType("char(36)"); + + b1.Property("UseDarkMode") + .HasColumnType("tinyint(1)"); + + b1.HasKey("ProfileId"); + + b1.ToTable("Profile"); + + b1.WithOwner() + .HasForeignKey("ProfileId"); + }); + + b.Navigation("Address"); + + b.Navigation("Settings") + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Address", b => + { + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => + { + b.Navigation("Customers"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.cs new file mode 100644 index 00000000..539a4bb9 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/20260412140549_Initial.cs @@ -0,0 +1,756 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.EntityEvents.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Address", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Street = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Address", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Person", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Identitifer = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Person", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Profile", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AddressId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + Settings_UseDarkMode = table.Column(type: "tinyint(1)", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Profile", x => x.Id); + table.ForeignKey( + name: "FK_Profile_Address_AddressId", + column: x => x.AddressId, + principalTable: "Address", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Customer", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProfileId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + Identitifer = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Customer", x => x.Id); + table.ForeignKey( + name: "FK_Customer_Profile_ProfileId", + column: x => x.ProfileId, + principalTable: "Profile", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Order", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CustomerId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Order", x => x.Id); + table.ForeignKey( + name: "FK_Order_Customer_CustomerId", + column: x => x.CustomerId, + principalTable: "Customer", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Address_CreatedAt", + table: "Address", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Address_IsDeleted", + table: "Address", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Customer_CreatedAt", + table: "Customer", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Customer_IsDeleted", + table: "Customer", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Customer_Name", + table: "Customer", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Customer_ProfileId", + table: "Customer", + column: "ProfileId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_CreatedAt", + table: "Order", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Order_CustomerId", + table: "Order", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_IsDeleted", + table: "Order", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Person_CreatedAt", + table: "Person", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Person_IsDeleted", + table: "Person", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Profile_CreatedAt", + table: "Profile", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Profile_IsDeleted", + table: "Profile", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "UX_Profile_AddressId", + table: "Profile", + column: "AddressId", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Order"); + + migrationBuilder.DropTable( + name: "Person"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "Customer"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + + migrationBuilder.DropTable( + name: "Profile"); + + migrationBuilder.DropTable( + name: "Address"); + } + } +} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..ce727baf --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,846 @@ +// +using System; +using Api.Data.EntityEvents.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.EntityEvents.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Street") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Address"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Identitifer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.HasIndex("ProfileId"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CustomerId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("CustomerId"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Person", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Identitifer") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Person"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AddressId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("AddressId") + .IsUnique() + .HasDatabaseName("UX_Profile_AddressId"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Profile"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => + { + b.HasOne("Api.Data.EntityEvents.Models.Profile", "Profile") + .WithMany("Customers") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Order", b => + { + b.HasOne("Api.Data.EntityEvents.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => + { + b.HasOne("Api.Data.EntityEvents.Models.Address", "Address") + .WithOne("Profile") + .HasForeignKey("Api.Data.EntityEvents.Models.Profile", "AddressId"); + + b.OwnsOne("Api.Data.EntityEvents.Models.Owned.ProfileSettings", "Settings", b1 => + { + b1.Property("ProfileId") + .HasColumnType("char(36)"); + + b1.Property("UseDarkMode") + .HasColumnType("tinyint(1)"); + + b1.HasKey("ProfileId"); + + b1.ToTable("Profile"); + + b1.WithOwner() + .HasForeignKey("ProfileId"); + }); + + b.Navigation("Address"); + + b.Navigation("Settings") + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Address", b => + { + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Api.Data.EntityEvents.Models.Profile", b => + { + b.Navigation("Customers"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Program.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Program.cs new file mode 100644 index 00000000..040619d0 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Program.cs @@ -0,0 +1,16 @@ +using Api.Data.EntityEvents.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; +using Nano.Eventing.Extensions; +using Nano.Eventing.RabbitMq; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + x.AddNanoEventing(); + }) + .Build() + .Run(); diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/Properties/InternalsVisibleTo.cs b/Api.Data.EntityEvents/Api.Data.EntityEvents/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..a49dcfd1 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.EntityEvents")] \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json new file mode 100644 index 00000000..74c8964f --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Development.json @@ -0,0 +1,12 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + }, + "Eventing": { + "Credentials": { + "Id": "rabbitmq_user", + "Secret": "password" + } + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Production.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Staging.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.json b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.json new file mode 100644 index 00000000..3402a745 --- /dev/null +++ b/Api.Data.EntityEvents/Api.Data.EntityEvents/appsettings.json @@ -0,0 +1,19 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + }, + "Eventing": { + "Host": "rabbitmq" + } +} \ No newline at end of file diff --git a/Api.Data.EntityEvents/Dockerfile b/Api.Data.EntityEvents/Dockerfile new file mode 100644 index 00000000..dca7eca5 --- /dev/null +++ b/Api.Data.EntityEvents/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.EntityEvents.dll"] \ No newline at end of file diff --git a/Api.Data.EntityEvents/LICENSE b/Api.Data.EntityEvents/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.EntityEvents/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.EntityEvents/README.md b/Api.Data.EntityEvents/README.md new file mode 100644 index 00000000..f783dcec --- /dev/null +++ b/Api.Data.EntityEvents/README.md @@ -0,0 +1,43 @@ +# Api.Data.EntityEvents + +> _Nano API application with data entity events._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to +demonstrate entity events. Additionally, eventing has been enabled mirroring the setup from **[Api.Eventing.RabbityMq](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Eventing.RabbityMq)**, +but any eventing provider can be used. New entity controllers have been added for use with showcasing entity events. + +An additional application has been added to the solution. In a typical architecture, this application would be placed in a separate solution. However, for the purpose of demonstrating +entity events in Nano, it has been included within the same solution. This setup allows the second application to receive and handle the published entity events from the first application, +enabling a complete end-to-end event flow for demonstration purposes. + +A set of entities has been introduced to demonstrate the eventing behavior. The core structure consists of a `Customer` entity that derives from `Person`. A `Customer` contains +a `Profile`, which in turn contains an `Address`. In addition, the `Customer` has a collection of `Order` entities, and the `Profile` includes an owned navigation property. The full +entity relationships can be inspected directly in the codebase. + +Also an `OnInserting` and `OnUpdating` trigger has been mapped for `Customer`, to show how the entity events will get the updated value. + +> 📖 Learn more about **[Nano Data Triggers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#triggers)**. + +Both `Person` and `Customer` are annotated with the `PublishAttribute`, and define property names that determine which fields should trigger publish events on added, modified, +and deleted actions. Several controllers have also been added to support these entities, enabling various create, update, and delete operations to trigger `Customer` entity events. When +a `Customer` is added, modified, or deleted directly, an event is published. However, changes to dependent navigation properties will also result in a `Modified` event being published +for the `Customer`. + +It is also important to note that `Customer` inherits publishable property definitions from `Person`. To ensure all property names defined across the entire inheritance hierarchy are +included, Nano aggregates the `PublishAttribute` metadata and hydrates entities both forward (for direct changes) and in reverse (via foreign key relationships) to capture deferred changes. + +> 📖 Learn more about **[Nano Data Entity Events](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#entity-events)**. diff --git a/Api.Data.EntityEvents/icon.png b/Api.Data.EntityEvents/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml b/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml new file mode 100644 index 00000000..d7305c76 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.identity.auth.apikey: + image: api.data.identity.auth.apikey + hostname: api-data-auth-apikey + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.Identity.Auth.ApiKey + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.Identity.Auth.ApiKey/.dockerignore b/Api.Data.Identity.Auth.ApiKey/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.Identity.Auth.ApiKey/.github/config/slack.yml b/Api.Data.Identity.Auth.ApiKey/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..e43c9804 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.github/workflows/build-and-deploy.yml @@ -0,0 +1,252 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.Identity.Auth.ApiKey + IMAGE_NAME: api.data.identity.auth.apikey + SERVICE_NAME: api-data-identity.auth.apikey + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + AUTH_API_KEY_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_API_KEY_SECRET || secrets.STAGING_AUTH_API_KEY_SECRET }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/auth-jwt-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-jwt-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-jwt-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/auth-apikey-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-apikey-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-apikey-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/.gitignore b/Api.Data.Identity.Auth.ApiKey/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-apikey-secret.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-apikey-secret.yaml new file mode 100644 index 00000000..45db9a37 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-apikey-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: auth-api-key-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + apikey-secret: %AUTH_API_KEY_SECRET% + diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-jwt-secret.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-jwt-secret.yaml new file mode 100644 index 00000000..3898973f --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-jwt-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: auth-jwt-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + jwt-public-key: %AUTH_JWT_PUBLIC_KEY% + jwt-private-key: %AUTH_JWT_PRIVATE_KEY% + diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-sql-secret.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/autoscaler.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml new file mode 100644 index 00000000..c8c3659c --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/deployment.yaml @@ -0,0 +1,99 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + - name: Data__Identity__ApiKey + valueFrom: + secretKeyRef: + name: auth-api-key-secret + key: apikey-secret + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.Identity.Auth.ApiKey/.kubernetes/service.yaml b/Api.Data.Identity.Auth.ApiKey/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Properties/DoNotParallelize.cs b/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Tests.Api.Data.Identity.Auth.ApiKey.csproj b/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Tests.Api.Data.Identity.Auth.ApiKey.csproj new file mode 100644 index 00000000..ceb6be59 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/.tests/Tests.Api.Data.Identity.Auth.ApiKey/Tests.Api.Data.Identity.Auth.ApiKey.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Api.Data.Identity.Auth.ApiKey.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Criterias/UserQueryCriteria.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Criterias/UserQueryCriteria.cs new file mode 100644 index 00000000..e2a95cb1 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/Criterias/UserQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.Identity.Auth.ApiKey.Models.Criterias; + +/// +public class UserQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(User.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/User.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/User.cs new file mode 100644 index 00000000..df611417 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.Models/User.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Identity.Auth.ApiKey.Models; + +/// +/// User. +/// +public class User : BaseEntityUser +{ + /// + /// Name. + /// + [Required] + [MaxLength(128)] + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.sln b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.sln new file mode 100644 index 00000000..058469a3 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.sln @@ -0,0 +1,150 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-apikey-secret.yaml = .kubernetes\auth-apikey-secret.yaml + .kubernetes\auth-jwt-secret.yaml = .kubernetes\auth-jwt-secret.yaml + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.ApiKey.Models", "Api.Data.Identity.Auth.ApiKey.Models\Api.Data.Identity.Auth.ApiKey.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.ApiKey", "Api.Data.Identity.Auth.ApiKey\Api.Data.Identity.Auth.ApiKey.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Identity.Auth.ApiKey", ".tests\Tests.Api.Data.Identity.Auth.ApiKey\Tests.Api.Data.Identity.Auth.ApiKey.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.csproj b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.csproj new file mode 100644 index 00000000..9255b1f9 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/AuthController.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/AuthController.cs new file mode 100644 index 00000000..4d792d7a --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/AuthController.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Api.Mvc.Authentication.Abstractions; + +namespace Api.Data.Identity.Auth.ApiKey.Controllers; + +/// +public class AuthController(ILogger logger, IAuthRepository authRepository) + : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/UsersController.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/UsersController.cs new file mode 100644 index 00000000..aced14ef --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Controllers/UsersController.cs @@ -0,0 +1,12 @@ +using Api.Data.Identity.Auth.ApiKey.Models; +using Api.Data.Identity.Auth.ApiKey.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; +using Nano.Data.Abstractions.Identity; + +namespace Api.Data.Identity.Auth.ApiKey.Controllers; + +/// +public class UsersController(ILogger logger, IRepository repository, IIdentityRepository identityRepository) + : BaseEntityUserController(logger, repository, identityRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/Mappings/UserMapping.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/Mappings/UserMapping.cs new file mode 100644 index 00000000..5da560ef --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/Mappings/UserMapping.cs @@ -0,0 +1,24 @@ +using System; +using Api.Data.Identity.Auth.ApiKey.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings.Identity; + +namespace Api.Data.Identity.Auth.ApiKey.Data.Mappings; + +/// +public class UserMapping : BaseEntityUserMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + if (builder == null) + throw new ArgumentNullException(nameof(builder)); + + base.Configure(builder); + + builder + .Property(x => x.Name) + .HasMaxLength(128) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContext.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContext.cs new file mode 100644 index 00000000..87e6c99b --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.Identity.Auth.ApiKey.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContextFactory.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..435fc1c7 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.Identity.Auth.ApiKey.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local new file mode 100644 index 00000000..bab003df --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.ApiKey.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.Designer.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.Designer.cs new file mode 100644 index 00000000..f68b351d --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.Designer.cs @@ -0,0 +1,660 @@ +// +using System; +using Api.Data.Identity.Auth.ApiKey.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Identity.Auth.ApiKey.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415143026_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Identity.Auth.ApiKey.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Identity.Auth.ApiKey.Models.User", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Api.Data.Identity.Auth.ApiKey.Models.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.cs new file mode 100644 index 00000000..87be3984 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/20260415143026_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Identity.Auth.ApiKey.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "User", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.Id); + table.ForeignKey( + name: "FK_User___EFIdentityUser_Id", + column: x => x.Id, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_User_CreatedAt", + table: "User", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_User_IsDeleted", + table: "User", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "User"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..0303f8b2 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,657 @@ +// +using System; +using Api.Data.Identity.Auth.ApiKey.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Identity.Auth.ApiKey.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Identity.Auth.ApiKey.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Identity.Auth.ApiKey.Models.User", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Api.Data.Identity.Auth.ApiKey.Models.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Program.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Program.cs new file mode 100644 index 00000000..6899a140 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.Identity.Auth.ApiKey.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Properties/InternalsVisibleTo.cs b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..eb7f4939 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.Identity.Auth.ApiKey")] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json new file mode 100644 index 00000000..223c8a7b --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Development.json @@ -0,0 +1,18 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.development", + "Audience": "nano.development", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", + "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV" + } + }, + "Documentation": { + } + }, + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Production.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Production.json new file mode 100644 index 00000000..8ec28d0d --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Production.json @@ -0,0 +1,10 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.production", + "Audience": "nano.production" + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Staging.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Staging.json new file mode 100644 index 00000000..0d8d49d3 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.Staging.json @@ -0,0 +1,10 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.staging", + "Audience": "nano.staging" + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.json b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.json new file mode 100644 index 00000000..719ba02e --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Api.Data.Identity.Auth.ApiKey/appsettings.json @@ -0,0 +1,60 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + }, + "Documentation": { + } + }, + "Data": { + "ConnectionString": null, + "Identity": { + "TokensExpiration": "24:00:00", + "UseAudit": "All", + "User": { + "IsUniqueEmailAddressRequired": true, + "IsUniquePhoneNumberRequired": false, + "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", + "DefaultRoles": [ + "administrator" + ] + }, + "SignIn": { + "RequireConfirmedEmail": false, + "RequireConfirmedPhoneNumber": false + }, + "Lockout": { + "AllowedForNewUsers": true, + "MaxFailedAccessAttempts": 3, + "DefaultLockoutTimeSpan": "00:30:00" + }, + "Password": { + "RequireDigit": true, + "RequireNonAlphanumeric": true, + "RequireLowercase": true, + "RequirUppercase": true, + "RequiredLength": 12, + "RequiredUniqueCharacters": 3 + }, + "ApiKey": { + "Secret": "secret" + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/Dockerfile b/Api.Data.Identity.Auth.ApiKey/Dockerfile new file mode 100644 index 00000000..30ab00dc --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.ApiKey.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/LICENSE b/Api.Data.Identity.Auth.ApiKey/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Identity.Auth.ApiKey/README.md b/Api.Data.Identity.Auth.ApiKey/README.md new file mode 100644 index 00000000..45050da2 --- /dev/null +++ b/Api.Data.Identity.Auth.ApiKey/README.md @@ -0,0 +1,86 @@ +# Api.Data.Identity.Auth.ApiKey + +> _Nano API application with data identity api-key authentication._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Data.Identity.Auth.Jwt](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.Identity.Auth.Jwt)**. + +The service has been configured for API key authentication. Nothing else has changed. It demonstrates how to manage API keys in Nano, and how to use both JWT and API key +authentication concurrently. Use the `auth/login/apikey` to authenticate and receive a JWT token, to use in subsequent requests. + +Try out the different API key identity and authentication methods. + +API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration +are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +The following endpoint from the auth controller is available for testing: + +| Endpoint | Description | +| -------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/auth/login/apikey` | Authenticates the user using `X-Api-Key` header value and returns an access token (JWT). | +| `http://localhost:8080/api/auth/login` | Authenticates a user and returns an access token (JWT). | +| `http://localhost:8080/api/auth/login/refresh` | Refreshes an existing access token. | +| `http://localhost:8080/api/auth/logout` | Logs out the current user. | + +Additionally, the identity controller is also avaialble, and the actions can be used for testing authorization. + +> 📖 Learn more about **[Nano API Key Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#authentication)**. + +## Configuration +Configured the application with the necessary authentication setup, in addition to the existing identity and jwt configuration. + +```json +"App": { + "Data": { + "Identity": { + "ApiKey": { + "Secret": "secret" + } + } + } +} +``` + +## Kubernetes +For `Staging` and `Production` environments, a secret must be created to securely store the apikey secret. Below demonstrates how to map the secret containing the API key secret. + +```yaml +spec: + template: + spec: + containers: + env: + - name: Data__Identity__ApiKey + valueFrom: + secretKeyRef: + name: auth-api-key-secret + key: apikey-secret +``` + +## GitHub Action +The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. + +```yaml +env: + AUTH_API_KEY_SECRET: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_API_KEY_SECRET || secrets.STAGING_AUTH_API_KEY_SECRET }} +``` + +...and created during the Kubernetes deploy step. diff --git a/Api.Data.Identity.Auth.ApiKey/icon.png b/Api.Data.Identity.Auth.ApiKey/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml b/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml new file mode 100644 index 00000000..921f1be5 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.identity.auth.external.custom: + image: api.data.identity.auth.external.custom + hostname: api-data-auth-external-custom + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.Identity.Auth.External.Custom + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.Identity.Auth.External.Custom/.dockerignore b/Api.Data.Identity.Auth.External.Custom/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.Identity.Auth.External.Custom/.github/config/slack.yml b/Api.Data.Identity.Auth.External.Custom/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..49b0d6db --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.github/workflows/build-and-deploy.yml @@ -0,0 +1,244 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.Identity.Auth.External.Custom + IMAGE_NAME: api.data.identity.auth.external.custom + SERVICE_NAME: api-data-identity.auth.external.custom + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/auth-jwt-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-jwt-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-jwt-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/.gitignore b/Api.Data.Identity.Auth.External.Custom/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-jwt-secret.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-jwt-secret.yaml new file mode 100644 index 00000000..3898973f --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-jwt-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: auth-jwt-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + jwt-public-key: %AUTH_JWT_PUBLIC_KEY% + jwt-private-key: %AUTH_JWT_PRIVATE_KEY% + diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-sql-secret.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/autoscaler.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml new file mode 100644 index 00000000..f615e1a7 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/deployment.yaml @@ -0,0 +1,94 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.Identity.Auth.External.Custom/.kubernetes/service.yaml b/Api.Data.Identity.Auth.External.Custom/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Properties/DoNotParallelize.cs b/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Tests.Api.Data.Identity.Auth.External.Custom.csproj b/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Tests.Api.Data.Identity.Auth.External.Custom.csproj new file mode 100644 index 00000000..f4bf561a --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/.tests/Tests.Api.Data.Identity.Auth.External.Custom/Tests.Api.Data.Identity.Auth.External.Custom.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Api.Data.Identity.Auth.External.Custom.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Criterias/UserQueryCriteria.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Criterias/UserQueryCriteria.cs new file mode 100644 index 00000000..0fa1f6cf --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/Criterias/UserQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.Identity.Auth.External.Custom.Models.Criterias; + +/// +public class UserQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(User.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/User.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/User.cs new file mode 100644 index 00000000..29ac4e15 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.Models/User.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Identity.Auth.External.Custom.Models; + +/// +/// User. +/// +public class User : BaseEntityUser +{ + /// + /// Name. + /// + [Required] + [MaxLength(128)] + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.sln b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.sln new file mode 100644 index 00000000..e7f0ea76 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.sln @@ -0,0 +1,149 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-jwt-secret.yaml = .kubernetes\auth-jwt-secret.yaml + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.External.Custom.Models", "Api.Data.Identity.Auth.External.Custom.Models\Api.Data.Identity.Auth.External.Custom.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.External.Custom", "Api.Data.Identity.Auth.External.Custom\Api.Data.Identity.Auth.External.Custom.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Identity.Auth.External.Custom", ".tests\Tests.Api.Data.Identity.Auth.External.Custom\Tests.Api.Data.Identity.Auth.External.Custom.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.csproj b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.csproj new file mode 100644 index 00000000..b3372b08 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs new file mode 100644 index 00000000..7dd866ed --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Authentication/ExternalProviderCustomRepository.cs @@ -0,0 +1,44 @@ +using System.Threading; +using System.Threading.Tasks; +using Nano.App.Api.Mvc.Authentication; +using Nano.Data.Abstractions.Identity.Authentication.Models; + +namespace Api.Data.Identity.Auth.External.Custom.Authentication; + +/// +public class ExternalProviderCustomRepository() : BaseAuthExternalRepository("Custom") +{ + /// + public override async Task AuthenticateAsync(ImplicitFlow flow, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return new ExternalAuthenticationData + { + Id = "external-id", + Username = "MyUser", + EmailAddress = "johndoe@domain.com", + PhoneNumber = "+4520111112", + Name = "John Doe", + ExternalToken = new ExternalAuthenticationToken + { + Name = this.ProviderName, + Token = "token", + RefreshToken = "refresh-token" + } + }; + } + + /// + public override async Task AuthenticateRefreshAsync(string refreshToken, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return new ExternalAuthenticationToken + { + Name = this.ProviderName, + Token = "token", + RefreshToken = "refresh-token" + }; + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/AuthController.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/AuthController.cs new file mode 100644 index 00000000..3e7aaac9 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/AuthController.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Api.Mvc.Authentication.Abstractions; + +namespace Api.Data.Identity.Auth.External.Custom.Controllers; + +/// +public class AuthController(ILogger logger, IAuthRepository authRepository) + : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/UsersController.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/UsersController.cs new file mode 100644 index 00000000..ff8a1dee --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Controllers/UsersController.cs @@ -0,0 +1,12 @@ +using Api.Data.Identity.Auth.External.Custom.Models; +using Api.Data.Identity.Auth.External.Custom.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; +using Nano.Data.Abstractions.Identity; + +namespace Api.Data.Identity.Auth.External.Custom.Controllers; + +/// +public class UsersController(ILogger logger, IRepository repository, IIdentityRepository identityRepository) + : BaseEntityUserController(logger, repository, identityRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/Mappings/UserMapping.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/Mappings/UserMapping.cs new file mode 100644 index 00000000..be5206c1 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/Mappings/UserMapping.cs @@ -0,0 +1,24 @@ +using System; +using Api.Data.Identity.Auth.External.Custom.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings.Identity; + +namespace Api.Data.Identity.Auth.External.Custom.Data.Mappings; + +/// +public class UserMapping : BaseEntityUserMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + if (builder == null) + throw new ArgumentNullException(nameof(builder)); + + base.Configure(builder); + + builder + .Property(x => x.Name) + .HasMaxLength(128) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContext.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContext.cs new file mode 100644 index 00000000..b8d78965 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.Identity.Auth.External.Custom.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContextFactory.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..53d00253 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.Identity.Auth.External.Custom.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local new file mode 100644 index 00000000..a00c6450 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.Designer.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.Designer.cs new file mode 100644 index 00000000..008df313 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.Designer.cs @@ -0,0 +1,660 @@ +// +using System; +using Api.Data.Identity.Auth.External.Custom.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Identity.Auth.External.Custom.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415143439_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Identity.Auth.External.Custom.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Identity.Auth.External.Custom.Models.User", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Api.Data.Identity.Auth.External.Custom.Models.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.cs new file mode 100644 index 00000000..1fef0424 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/20260415143439_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Identity.Auth.External.Custom.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "User", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.Id); + table.ForeignKey( + name: "FK_User___EFIdentityUser_Id", + column: x => x.Id, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_User_CreatedAt", + table: "User", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_User_IsDeleted", + table: "User", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "User"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..9bd7b87b --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,657 @@ +// +using System; +using Api.Data.Identity.Auth.External.Custom.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Identity.Auth.External.Custom.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Identity.Auth.External.Custom.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Identity.Auth.External.Custom.Models.User", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Api.Data.Identity.Auth.External.Custom.Models.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Program.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Program.cs new file mode 100644 index 00000000..3a3caa58 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.Identity.Auth.External.Custom.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Properties/InternalsVisibleTo.cs b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..0865fd1f --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.Identity.Auth.External.Custom")] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json new file mode 100644 index 00000000..223c8a7b --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Development.json @@ -0,0 +1,18 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.development", + "Audience": "nano.development", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", + "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV" + } + }, + "Documentation": { + } + }, + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Production.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Production.json new file mode 100644 index 00000000..8ec28d0d --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Production.json @@ -0,0 +1,10 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.production", + "Audience": "nano.production" + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Staging.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Staging.json new file mode 100644 index 00000000..0d8d49d3 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.Staging.json @@ -0,0 +1,10 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.staging", + "Audience": "nano.staging" + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.json b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.json new file mode 100644 index 00000000..2494e2d7 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Api.Data.Identity.Auth.External.Custom/appsettings.json @@ -0,0 +1,57 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + }, + "Documentation": { + } + }, + "Data": { + "ConnectionString": null, + "Identity": { + "TokensExpiration": "24:00:00", + "UseAudit": "All", + "User": { + "IsUniqueEmailAddressRequired": true, + "IsUniquePhoneNumberRequired": false, + "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", + "DefaultRoles": [ + "administrator" + ] + }, + "SignIn": { + "RequireConfirmedEmail": false, + "RequireConfirmedPhoneNumber": false + }, + "Lockout": { + "AllowedForNewUsers": true, + "MaxFailedAccessAttempts": 3, + "DefaultLockoutTimeSpan": "00:30:00" + }, + "Password": { + "RequireDigit": true, + "RequireNonAlphanumeric": true, + "RequireLowercase": true, + "RequirUppercase": true, + "RequiredLength": 12, + "RequiredUniqueCharacters": 3 + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/Dockerfile b/Api.Data.Identity.Auth.External.Custom/Dockerfile new file mode 100644 index 00000000..5a2e6a5e --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.External.Custom.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/LICENSE b/Api.Data.Identity.Auth.External.Custom/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Identity.Auth.External.Custom/README.md b/Api.Data.Identity.Auth.External.Custom/README.md new file mode 100644 index 00000000..f9e7730c --- /dev/null +++ b/Api.Data.Identity.Auth.External.Custom/README.md @@ -0,0 +1,86 @@ +# Api.Data.Identity.Auth.External.Custom + +> _Nano API application with data identity external custom authentication._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Data.Identity.Auth.Jwt](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.Identity.Auth.Jwt)**. + +The JWT authentication scheme remains configured, and a `BaseAuthExternalRepository` implementation named `ExternalProviderCustomRepository`, with `Custom` as provider-name, +has been added. As a result, additional endpoints from the `AuthController` are now exposed, as shown below. + +The `ExternalProviderCustomRepository` always succeeds and returns static (mocked) data. It serves as a simple example of how to implement a custom external authentication +provider in Nano. + +API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration +are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +The following endpoint from the auth controller is available for testing: + +| Endpoint | Description | +| ------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| `http://localhost:8080/api/auth/external/schemes` | Retrieves all configured external authentication methods, e.g., Google, Facebook. | +| `http://localhost:8080/api/auth/login` | Authenticates a user and returns an access token (JWT). | +| `http://localhost:8080/api/auth/login/external/custom` | Signs in a user using external custom authentication | +| `http://localhost:8080/api/auth/login/refresh` | Refreshes an existing access token. | +| `http://localhost:8080/api/auth/logout` | Logs out the current user. | + +The following new endpoints related to the custom authentication provider from the identity controller is shown below. + +| Endpoint | Description | +| --------------------------------------------------------------------- | ---------------------------------------------------------- | +| `http://localhost:8080/api/users/signup/external/custom` | Signs up a user using external custom authentication | +| `http://localhost:8080/api/users/{id}/external-logins` | Retrieves all external authentication methods of a user. | +| `http://localhost:8080/api/users/{id}/external-logins/add/custom` | Adds a `Custom` external login to a user account. | +| `http://localhost:8080/api/users/{id}/external-logins/remove/custom` | Removes an `Custom` login from a user account. | + +> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#authentication)**. + +## Configuration +Configured the application with the necessary authentication setup, in addition to the identity configuration. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + } +} +``` + +...and for `appesettings.Developmnet.json`. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "Development.nano", + "Audience": "Development.nano", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", + "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", + "Expiration": "24:00:00" + } + } +} +``` diff --git a/Api.Data.Identity.Auth.External.Custom/icon.png b/Api.Data.Identity.Auth.External.Custom/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml b/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml new file mode 100644 index 00000000..69136e45 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.identity.auth.jwt: + image: api.data.identity.auth.jwt + hostname: api-data-auth-jwt + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.Identity.Auth.Jwt + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.Identity.Auth.Jwt/.dockerignore b/Api.Data.Identity.Auth.Jwt/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.Identity.Auth.Jwt/.github/config/slack.yml b/Api.Data.Identity.Auth.Jwt/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml b/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..7141f0f2 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.github/workflows/build-and-deploy.yml @@ -0,0 +1,244 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.Identity.Auth.Jwt + IMAGE_NAME: api.data.identity.auth.jwt + SERVICE_NAME: api-data-identity.auth.jwt + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/auth-jwt-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-jwt-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-jwt-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/.gitignore b/Api.Data.Identity.Auth.Jwt/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-jwt-secret.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-jwt-secret.yaml new file mode 100644 index 00000000..3898973f --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-jwt-secret.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Secret +metadata: + name: auth-jwt-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + jwt-public-key: %AUTH_JWT_PUBLIC_KEY% + jwt-private-key: %AUTH_JWT_PRIVATE_KEY% + diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-sql-secret.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/autoscaler.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml new file mode 100644 index 00000000..f615e1a7 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/deployment.yaml @@ -0,0 +1,94 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.Identity.Auth.Jwt/.kubernetes/service.yaml b/Api.Data.Identity.Auth.Jwt/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Properties/DoNotParallelize.cs b/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Tests.Api.Data.Identity.Auth.Jwt.csproj b/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Tests.Api.Data.Identity.Auth.Jwt.csproj new file mode 100644 index 00000000..50c76a68 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/.tests/Tests.Api.Data.Identity.Auth.Jwt/Tests.Api.Data.Identity.Auth.Jwt.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Api.Data.Identity.Auth.Jwt.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Criterias/UserQueryCriteria.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Criterias/UserQueryCriteria.cs new file mode 100644 index 00000000..671a3309 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/Criterias/UserQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.Identity.Auth.Jwt.Models.Criterias; + +/// +public class UserQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(User.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/User.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/User.cs new file mode 100644 index 00000000..11e267fb --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.Models/User.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Identity.Auth.Jwt.Models; + +/// +/// User. +/// +public class User : BaseEntityUser +{ + /// + /// Name. + /// + [Required] + [MaxLength(128)] + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.sln b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.sln new file mode 100644 index 00000000..39eeee77 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.sln @@ -0,0 +1,149 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-jwt-secret.yaml = .kubernetes\auth-jwt-secret.yaml + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.Jwt.Models", "Api.Data.Identity.Auth.Jwt.Models\Api.Data.Identity.Auth.Jwt.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Auth.Jwt", "Api.Data.Identity.Auth.Jwt\Api.Data.Identity.Auth.Jwt.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Identity.Auth.Jwt", ".tests\Tests.Api.Data.Identity.Auth.Jwt\Tests.Api.Data.Identity.Auth.Jwt.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.csproj b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.csproj new file mode 100644 index 00000000..165b0594 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/AuthController.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/AuthController.cs new file mode 100644 index 00000000..78c56093 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/AuthController.cs @@ -0,0 +1,9 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Api.Mvc.Authentication.Abstractions; + +namespace Api.Data.Identity.Auth.Jwt.Controllers; + +/// +public class AuthController(ILogger logger, IAuthRepository authRepository) + : BaseAuthController(logger, authRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/UsersController.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/UsersController.cs new file mode 100644 index 00000000..b0695aa1 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Controllers/UsersController.cs @@ -0,0 +1,12 @@ +using Api.Data.Identity.Auth.Jwt.Models; +using Api.Data.Identity.Auth.Jwt.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; +using Nano.Data.Abstractions.Identity; + +namespace Api.Data.Identity.Auth.Jwt.Controllers; + +/// +public class UsersController(ILogger logger, IRepository repository, IIdentityRepository identityRepository) + : BaseEntityUserController(logger, repository, identityRepository); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/Mappings/UserMapping.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/Mappings/UserMapping.cs new file mode 100644 index 00000000..7ee8ae00 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/Mappings/UserMapping.cs @@ -0,0 +1,24 @@ +using System; +using Api.Data.Identity.Auth.Jwt.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings.Identity; + +namespace Api.Data.Identity.Auth.Jwt.Data.Mappings; + +/// +public class UserMapping : BaseEntityUserMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + if (builder == null) + throw new ArgumentNullException(nameof(builder)); + + base.Configure(builder); + + builder + .Property(x => x.Name) + .HasMaxLength(128) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContext.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContext.cs new file mode 100644 index 00000000..acc59d18 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.Identity.Auth.Jwt.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContextFactory.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..2c77987f --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.Identity.Auth.Jwt.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local new file mode 100644 index 00000000..678ca81b --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.Jwt.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.Designer.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.Designer.cs new file mode 100644 index 00000000..da7e84cb --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.Designer.cs @@ -0,0 +1,660 @@ +// +using System; +using Api.Data.Identity.Auth.Jwt.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Identity.Auth.Jwt.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415143540_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Identity.Auth.Jwt.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Identity.Auth.Jwt.Models.User", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Api.Data.Identity.Auth.Jwt.Models.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.cs new file mode 100644 index 00000000..724944a8 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/20260415143540_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Identity.Auth.Jwt.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "User", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.Id); + table.ForeignKey( + name: "FK_User___EFIdentityUser_Id", + column: x => x.Id, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_User_CreatedAt", + table: "User", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_User_IsDeleted", + table: "User", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "User"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..617580b9 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,657 @@ +// +using System; +using Api.Data.Identity.Auth.Jwt.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Identity.Auth.Jwt.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Identity.Auth.Jwt.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Identity.Auth.Jwt.Models.User", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Api.Data.Identity.Auth.Jwt.Models.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Program.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Program.cs new file mode 100644 index 00000000..2d2c524b --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.Identity.Auth.Jwt.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Properties/InternalsVisibleTo.cs b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..2cb6248c --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.Identity.Auth.Jwt")] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json new file mode 100644 index 00000000..223c8a7b --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Development.json @@ -0,0 +1,18 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.development", + "Audience": "nano.development", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQAB", + "PrivateKey": "MIIEowIBAAKCAQEAv7iVNUS5wT7Fvg/hkmlvvPnOW7Rcyh7dFStJSTtM+7f74+GGVJLl6spXasnsQ7v6rw7vlyb+uVk1UaQsUA38luSNGWfPqc3JAtkeJPWCu1kN79Yo3im7Qx6B1u4gf0AR3n86ClQGz3O5Jxo8M3+zlwveYnlf6bqhBakOVdPS5tX0Bvh/F9lXiEF53EZEcfuHjBjDLik9PUdTjqehPLCPyI1/FbfE8P1Y4S7AEfs2fIqXGxJNXDyoDRvi42qefqXcsmzBUDYtHqvwSHWcDn5DXDRY2FYkyESMvd7RRGwI6U0g8V9k3Qudd4LjQTs8LdBu5u25wvqx37Y1518BPqGQkQIDAQABAoIBAEwNH3sS+RCUIwLC7/sRQhbXjSlJgalX1uFH23lmQaJ0mEIMOyofX37kpwqgcM1pqwZ4SUhPWqoRnhn1ovJaqgD9Ro92Y6T7EarEj7Wfgi1pJSMnc+y05yi32E93BIMV2kDFfTONo2n1gNPnD0xqcsYPGjc76HUh6DADoMEhFr8kHaz4J2daKV0tJjApNt2oabk8BLQEq9Uv22DsLfL+nEOHPhSMk7EmNv3QQgUNH5ugeDNfTNr+A6K8YMbVVrmDalZS/GBWSscnJ9Ma2WrHJ/x2IRQECVMf6U05vrgtKb9imPcN09ccItIzcK/8ZBbSw2v+Gzf1Je447SYT9njAOiUCgYEAzOAsty4cxCLSWt2GBTE58MoThNeiVRBvc6Gw5B1olCCnWkVxRDYwYlPnwvemqa+YsfijrjVkuS0kJmfrGJ/MkV8Wsx2XL6mRyCBXOUog0U/Nh20ANU8kcmEMkGVtxDUM8hr9QQ5qex/LmSiy8YG4c4mfD6s7KvWnRxJcviXmgUMCgYEA75AQssujQtycWx6fZ/aBQLc6+xSlGaY73k2R8XLwMSASAeq1erxCSsuPF5lPRnQ4VZyfSOV9AcOyLgeJCi4ePJEnfZZMcGkKNt2yMsZoWUlJSmHIXhEEfKqu8Qo0TRu4/vQYPKwTVXdpbZJIlDgzztPdC1gOpCg3QQH16wPyL5sCgYAQ5Ygqj14F+w04Oz7bXMT3i+LyOMqFk3Ztpe8t0RMX7F2A/2spAgMZiOv7U2tmYToJq4TsUDD/aK6rkDR+cmdvsdTwbsdSQfzo8WngKrHsMVW1DpNO0jkiSci8e/EClpF7wigS3np/rw6ekhG4A0fQF5CLvUaC84GZRfVqJTwOewKBgQC4oTKNae54oGgMvewjBtOU2eKmEcIwo3JuoSACkw/U/J+ERKz7W85HsNymVmzHotir+pq0ZtHSI03Wtc4DP4nkKgbifoyI8huCL5igE1PmxFms7vGqtbjcj/tmH/QxHVWVgPCRChmYfACQBvbS7QHYvGYW0RXvpGL5QhaSuybTUwKBgG/p/gsj6yUDAiNhEWpSsMWl/3xJeIobcnH1XQrrXWIzL1xZtX1EkcqLM6++Ojjre3UKj96ZDFRpJH4uxTilE9MDOOf+PLoL01rr1rmzaWDr5NsI3nqz2AS6ZSuofO0rs7nQlKtTnQY0vlzPGqfQp4uQ11KPzO2PB9TEGwnZy5HV" + } + }, + "Documentation": { + } + }, + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Production.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Production.json new file mode 100644 index 00000000..8ec28d0d --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Production.json @@ -0,0 +1,10 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.production", + "Audience": "nano.production" + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Staging.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Staging.json new file mode 100644 index 00000000..0d8d49d3 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.Staging.json @@ -0,0 +1,10 @@ +{ + "App": { + "Authentication": { + "Jwt": { + "Issuer": "nano.staging", + "Audience": "nano.staging" + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.json b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.json new file mode 100644 index 00000000..2494e2d7 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Api.Data.Identity.Auth.Jwt/appsettings.json @@ -0,0 +1,57 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + }, + "Documentation": { + } + }, + "Data": { + "ConnectionString": null, + "Identity": { + "TokensExpiration": "24:00:00", + "UseAudit": "All", + "User": { + "IsUniqueEmailAddressRequired": true, + "IsUniquePhoneNumberRequired": false, + "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", + "DefaultRoles": [ + "administrator" + ] + }, + "SignIn": { + "RequireConfirmedEmail": false, + "RequireConfirmedPhoneNumber": false + }, + "Lockout": { + "AllowedForNewUsers": true, + "MaxFailedAccessAttempts": 3, + "DefaultLockoutTimeSpan": "00:30:00" + }, + "Password": { + "RequireDigit": true, + "RequireNonAlphanumeric": true, + "RequireLowercase": true, + "RequirUppercase": true, + "RequiredLength": 12, + "RequiredUniqueCharacters": 3 + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/Dockerfile b/Api.Data.Identity.Auth.Jwt/Dockerfile new file mode 100644 index 00000000..d166ebde --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.Identity.Auth.Jwt.dll"] \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/LICENSE b/Api.Data.Identity.Auth.Jwt/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Identity.Auth.Jwt/README.md b/Api.Data.Identity.Auth.Jwt/README.md new file mode 100644 index 00000000..bfbaf354 --- /dev/null +++ b/Api.Data.Identity.Auth.Jwt/README.md @@ -0,0 +1,118 @@ +# Api.Data.Identity.Auth.Jwt + +> _Nano API application with data identity jwt authentication._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Data.Identity](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.Identity)** and adds a derived `AuthController`. + +Nothing else has changed for this example. The derived `AuthController` enables the identity users in the application to use the three endpoints inherited from +the `BaseAuthController`. + +API documentation has been configured to make it easier to explore the available actions in the `AuthController`. Any actions that are not enabled due to omitted configuration +are automatically excluded. The API documentation is available at: **http://localhost:8080/docs**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +The following endpoint from the auth controller is available for testing: + +| Endpoint | Description | +| -------------------------------------------------- | ---------------------------------------------------------------------- | +| `http://localhost:8080/api/auth/login` | Authenticates a user and returns an access token (JWT). | +| `http://localhost:8080/api/auth/login/refresh` | Refreshes an existing access token. | +| `http://localhost:8080/api/auth/logout` | Logs out the current user. | + +Additionally, the identity controller is also avaialble, and the actions can be used for testing authorization. + +> 📖 Learn more about **[Nano Authentication](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#authentication)**. + +## Configuration +Configured the application with the necessary authentication setup, in addition to the identity configuration. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": null, + "Audience": null, + "PublicKey": null, + "PrivateKey": null, + "Expiration": "01:00:00", + "RefreshExpiration": "72:00:00" + } + } +} +``` + +...and for `appesettings.Developmnet.json`. + +```json +"App": { + "Authentication": { + "Jwt": { + "Issuer": "Development.nano", + "Audience": "Development.nano", + "PublicKey": "MIIBCgKCAQEAv7iVNUS5w...", + "PrivateKey": "MIIEowIBAAKCAQEAv7iV...", + "Expiration": "24:00:00" + } + } +} +``` + +## Kubernetes +For `Staging` and `Production` environments, a secret must be created to securely store the public and private keys, and optionally the credentials for `RootLogin` if it shoud be +enabled. Below demonstrates how to map the secret containing the JWT keys. + +```yaml +spec: + template: + spec: + containers: + env: + - name: App__Authentication__Jwt__PublicKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-public-key + - name: App__Authentication__Jwt__PrivateKey + valueFrom: + secretKeyRef: + name: auth-jwt-secret + key: jwt-private-key +``` + +## GitHub Action +The secrets defined in GitHub must also be mapped for the `Staging` and `Production` environments in the `build-and-deploy.yml` workflow, as shown below. + +```yaml +env: + AUTH_JWT_PUBLIC_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PUBLIC_KEY || secrets.STAGING_AUTH_JWT_PUBLIC_KEY }} + AUTH_JWT_PRIVATE_KEY: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AUTH_JWT_PRIVATE_KEY || secrets.STAGING_AUTH_JWT_PRIVATE_KEY }} +``` + +...and created during the Kubernetes deploy step. + +```yaml +sudo kubectl create secret generic auth-jwt-secret --from-literal=jwt-public-key=$env:AUTH_JWT_PUBLIC_KEY --from-literal=jwt-private-key=$env:AUTH_JWT_PRIVATE_KEY --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; +if ($LastExitCode -ne 0) +{ + throw "error"; +}; +``` + diff --git a/Api.Data.Identity.Auth.Jwt/icon.png b/Api.Data.Identity.Auth.Jwt/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.Identity/.docker/docker-compose.yml b/Api.Data.Identity/.docker/docker-compose.yml new file mode 100644 index 00000000..d4a7e47a --- /dev/null +++ b/Api.Data.Identity/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.identity: + image: api.data.identity + hostname: api-data-identity + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.Identity + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.Identity/.dockerignore b/Api.Data.Identity/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.Identity/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.Identity/.github/config/slack.yml b/Api.Data.Identity/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.Identity/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Identity/.github/workflows/build-and-deploy.yml b/Api.Data.Identity/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..ec82c6b7 --- /dev/null +++ b/Api.Data.Identity/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.Identity + IMAGE_NAME: api.data.identity + SERVICE_NAME: api-data-identity + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Identity/.gitignore b/Api.Data.Identity/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.Identity/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Identity/.kubernetes/auth-sql-secret.yaml b/Api.Data.Identity/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.Identity/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.Identity/.kubernetes/autoscaler.yaml b/Api.Data.Identity/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.Identity/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Identity/.kubernetes/configmap.yaml b/Api.Data.Identity/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.Identity/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Identity/.kubernetes/deployment.yaml b/Api.Data.Identity/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.Identity/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.Identity/.kubernetes/service.yaml b/Api.Data.Identity/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.Identity/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Properties/DoNotParallelize.cs b/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Tests.Api.Data.Identity.csproj b/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Tests.Api.Data.Identity.csproj new file mode 100644 index 00000000..449cc3ac --- /dev/null +++ b/Api.Data.Identity/.tests/Tests.Api.Data.Identity/Tests.Api.Data.Identity.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj b/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity.Models/Api.Data.Identity.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity.Models/Criterias/UserQueryCriteria.cs b/Api.Data.Identity/Api.Data.Identity.Models/Criterias/UserQueryCriteria.cs new file mode 100644 index 00000000..d3bdb438 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity.Models/Criterias/UserQueryCriteria.cs @@ -0,0 +1,34 @@ +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; +using System.Collections.Generic; +using System.Linq; + +namespace Api.Data.Identity.Models.Criterias; + +/// +public class UserQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(User.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity.Models/User.cs b/Api.Data.Identity/Api.Data.Identity.Models/User.cs new file mode 100644 index 00000000..ada9f5d6 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity.Models/User.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Identity.Models; + +/// +/// User. +/// +public class User : BaseEntityUser +{ + /// + /// Name. + /// + [Required] + [MaxLength(128)] + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity.sln b/Api.Data.Identity/Api.Data.Identity.sln new file mode 100644 index 00000000..ee1f6dac --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity.Models", "Api.Data.Identity.Models\Api.Data.Identity.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Identity", "Api.Data.Identity\Api.Data.Identity.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Identity", ".tests\Tests.Api.Data.Identity\Tests.Api.Data.Identity.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.Identity/Api.Data.Identity/Api.Data.Identity.csproj b/Api.Data.Identity/Api.Data.Identity/Api.Data.Identity.csproj new file mode 100644 index 00000000..308426b0 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Api.Data.Identity.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Controllers/UsersController.cs b/Api.Data.Identity/Api.Data.Identity/Controllers/UsersController.cs new file mode 100644 index 00000000..6d14a4cc --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Controllers/UsersController.cs @@ -0,0 +1,12 @@ +using Api.Data.Identity.Models; +using Api.Data.Identity.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; +using Nano.Data.Abstractions.Identity; + +namespace Api.Data.Identity.Controllers; + +/// +public class UsersController(ILogger logger, IRepository repository, IIdentityRepository identityRepository) + : BaseEntityUserController(logger, repository, identityRepository); \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Data/Mappings/UserMapping.cs b/Api.Data.Identity/Api.Data.Identity/Data/Mappings/UserMapping.cs new file mode 100644 index 00000000..e910480b --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Data/Mappings/UserMapping.cs @@ -0,0 +1,24 @@ +using System; +using Api.Data.Identity.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings.Identity; + +namespace Api.Data.Identity.Data.Mappings; + +/// +public class UserMapping : BaseEntityUserMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + if (builder == null) + throw new ArgumentNullException(nameof(builder)); + + base.Configure(builder); + + builder + .Property(x => x.Name) + .HasMaxLength(128) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContext.cs b/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContext.cs new file mode 100644 index 00000000..715d283c --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.Identity.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContextFactory.cs b/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..db58e2c0 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.Identity.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local b/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local new file mode 100644 index 00000000..a05f0920 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.Identity.dll"] \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.Designer.cs b/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.Designer.cs new file mode 100644 index 00000000..b46e5b7a --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.Designer.cs @@ -0,0 +1,660 @@ +// +using System; +using Api.Data.Identity.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Identity.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415142847_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Identity.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Identity.Models.User", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Api.Data.Identity.Models.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.cs b/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.cs new file mode 100644 index 00000000..5bda95ad --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Migrations/20260415142847_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Identity.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "User", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.Id); + table.ForeignKey( + name: "FK_User___EFIdentityUser_Id", + column: x => x.Id, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_User_CreatedAt", + table: "User", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_User_IsDeleted", + table: "User", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "User"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.Identity/Api.Data.Identity/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Identity/Api.Data.Identity/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..0478fbe9 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,657 @@ +// +using System; +using Api.Data.Identity.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Identity.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Identity.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Identity.Models.User", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Api.Data.Identity.Models.User", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Identity/Api.Data.Identity/Program.cs b/Api.Data.Identity/Api.Data.Identity/Program.cs new file mode 100644 index 00000000..d4ab1f42 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.Identity.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.Identity/Api.Data.Identity/Properties/InternalsVisibleTo.cs b/Api.Data.Identity/Api.Data.Identity/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..4da322e5 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.Identity")] \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json b/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.Production.json b/Api.Data.Identity/Api.Data.Identity/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.Staging.json b/Api.Data.Identity/Api.Data.Identity/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Identity/Api.Data.Identity/appsettings.json b/Api.Data.Identity/Api.Data.Identity/appsettings.json new file mode 100644 index 00000000..1e2f21b9 --- /dev/null +++ b/Api.Data.Identity/Api.Data.Identity/appsettings.json @@ -0,0 +1,47 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Documentation": { + } + }, + "Data": { + "ConnectionString": null, + "Identity": { + "TokensExpiration": "24:00:00", + "UseAudit": "All", + "User": { + "IsUniqueEmailAddressRequired": true, + "IsUniquePhoneNumberRequired": false, + "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", + "DefaultRoles": [ + "administrator" + ] + }, + "SignIn": { + "RequireConfirmedEmail": false, + "RequireConfirmedPhoneNumber": false + }, + "Lockout": { + "AllowedForNewUsers": true, + "MaxFailedAccessAttempts": 3, + "DefaultLockoutTimeSpan": "00:30:00" + }, + "Password": { + "RequireDigit": true, + "RequireNonAlphanumeric": true, + "RequireLowercase": true, + "RequirUppercase": true, + "RequiredLength": 12, + "RequiredUniqueCharacters": 3 + } + } + } +} \ No newline at end of file diff --git a/Api.Data.Identity/Dockerfile b/Api.Data.Identity/Dockerfile new file mode 100644 index 00000000..0e6712aa --- /dev/null +++ b/Api.Data.Identity/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.Identity.dll"] \ No newline at end of file diff --git a/Api.Data.Identity/LICENSE b/Api.Data.Identity/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.Identity/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Identity/README.md b/Api.Data.Identity/README.md new file mode 100644 index 00000000..4edd3d67 --- /dev/null +++ b/Api.Data.Identity/README.md @@ -0,0 +1,75 @@ +# Api.Data.Identity + +> _Nano API application with data identity._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to +demonstrate repository autosave. Entity controllers have been simplified to showcase identity; full controllers are unnecessary. + +The `User` entity model, `UserMapping` data mapping, `UserQueryCriteria` query criteria and the `UsersControlller` has been added to the solutiin. The controller is deriving +from the `BaseIdentityContrlller`, epxosing all configured identity actions. Everything needed to expose identity controller actions. + +The application is configured to audit all identity models. + +Also, API documentation has been configured, in order to easier see which audit endpoints are available. It can be accessed +here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +Not all identity actions are enabled in this example. It demonstrates identity functionality without any authentication enabled. Other lessons cover the different supported +authentication methods and the corresponding identity actions to manage them. + +> 📖 Learn more about **[Nano Data Identity](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#identity)**. + +## Configuration +The data identity has been configured for the application. The `UseAudit` ahs been set to `All` in order to audit log all identity changes. Normally, you would probably be more +selective in that option and choose some identityt model to audit. + +```json +"Data": { + "ConnectionString": null, + "Identity": { + "TokensExpiration": "24:00:00", + "UseAudit": "All", + "User": { + "IsUniqueEmailAddressRequired": true, + "IsUniquePhoneNumberRequired": false, + "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+", + "DefaultRoles": [ + "administrator" + ] + }, + "SignIn": { + "RequireConfirmedEmail": false, + "RequireConfirmedPhoneNumber": false + }, + "Lockout": { + "AllowedForNewUsers": true, + "MaxFailedAccessAttempts": 3, + "DefaultLockoutTimeSpan": "00:30:00" + }, + "Password": { + "RequireDigit": true, + "RequireNonAlphanumeric": true, + "RequireLowercase": true, + "RequirUppercase": true, + "RequiredLength": 12, + "RequiredUniqueCharacters": 3 + } + } +} +``` diff --git a/Api.Data.Identity/icon.png b/Api.Data.Identity/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.InMemory/.docker/docker-compose.yml b/Api.Data.InMemory/.docker/docker-compose.yml new file mode 100644 index 00000000..6a0d4084 --- /dev/null +++ b/Api.Data.InMemory/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.data.inmemory: + image: api.data.inmemory + hostname: api-data-inmemory + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.InMemory + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.InMemory/.dockerignore b/Api.Data.InMemory/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.InMemory/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.InMemory/.github/config/slack.yml b/Api.Data.InMemory/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.InMemory/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.InMemory/.github/workflows/build-and-deploy.yml b/Api.Data.InMemory/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..50a2894a --- /dev/null +++ b/Api.Data.InMemory/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.InMemory + IMAGE_NAME: api.data.inmemory + SERVICE_NAME: api-data-inmemory + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.InMemory/.gitignore b/Api.Data.InMemory/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.InMemory/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.InMemory/.kubernetes/autoscaler.yaml b/Api.Data.InMemory/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.InMemory/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.InMemory/.kubernetes/configmap.yaml b/Api.Data.InMemory/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.InMemory/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.InMemory/.kubernetes/deployment.yaml b/Api.Data.InMemory/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Data.InMemory/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.InMemory/.kubernetes/service.yaml b/Api.Data.InMemory/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.InMemory/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Properties/DoNotParallelize.cs b/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Tests.Api.Data.InMemory.csproj b/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Tests.Api.Data.InMemory.csproj new file mode 100644 index 00000000..1fab9bfa --- /dev/null +++ b/Api.Data.InMemory/.tests/Tests.Api.Data.InMemory/Tests.Api.Data.InMemory.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj b/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj new file mode 100644 index 00000000..f949c7de --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory.Models/Api.Data.InMemory.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..859c7482 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.InMemory.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith("Name", this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/Example.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/Example.cs new file mode 100644 index 00000000..5bd8e646 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.InMemory.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatable.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatable.cs new file mode 100644 index 00000000..14f5fe12 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.InMemory.Models; + +/// +/// Example. +/// +public class ExampleCreatable : BaseEntityCreatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatableAndEditable.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatableAndEditable.cs new file mode 100644 index 00000000..015ffeef --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleCreatableAndEditable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.InMemory.Models; + +/// +/// Example. +/// +public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleDeletable.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleDeletable.cs new file mode 100644 index 00000000..29dd2690 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleDeletable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.InMemory.Models; + +/// +/// Example. +/// +public class ExampleDeletable : BaseEntityDeletable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleUpdatable.cs b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleUpdatable.cs new file mode 100644 index 00000000..13d2e0e5 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory.Models/ExampleUpdatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.InMemory.Models; + +/// +/// Example. +/// +public class ExampleUpdatable : BaseEntityUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory.sln b/Api.Data.InMemory/Api.Data.InMemory.sln new file mode 100644 index 00000000..c33a403f --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory.sln @@ -0,0 +1,147 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.InMemory.Models", "Api.Data.InMemory.Models\Api.Data.InMemory.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.InMemory", "Api.Data.InMemory\Api.Data.InMemory.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.InMemory", ".tests\Tests.Api.Data.InMemory\Tests.Api.Data.InMemory.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.InMemory", "..\..\Nano.Library\Nano.Data.InMemory\Nano.Data.InMemory.csproj", "{C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.InMemory/Api.Data.InMemory/Api.Data.InMemory.csproj b/Api.Data.InMemory/Api.Data.InMemory/Api.Data.InMemory.csproj new file mode 100644 index 00000000..90828eef --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Api.Data.InMemory.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreatableAndEditableController.cs new file mode 100644 index 00000000..ac30d52f --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreatableAndEditableController.cs @@ -0,0 +1,15 @@ +using Api.Data.InMemory.Models; +using Api.Data.InMemory.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.InMemory.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) + : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreateablesController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreateablesController.cs new file mode 100644 index 00000000..cf47b529 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleCreateablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.InMemory.Models; +using Api.Data.InMemory.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.InMemory.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreateablesController(ILogger logger, IRepository repository) + : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleDeletablesController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleDeletablesController.cs new file mode 100644 index 00000000..d0c2d534 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleDeletablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.InMemory.Models; +using Api.Data.InMemory.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.InMemory.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleDeletablesController(ILogger logger, IRepository repository) + : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleUpdatablesController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleUpdatablesController.cs new file mode 100644 index 00000000..e0a3b5b4 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExampleUpdatablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.InMemory.Models; +using Api.Data.InMemory.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.InMemory.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleUpdatablesController(ILogger logger, IRepository repository) + : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExamplesController.cs b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExamplesController.cs new file mode 100644 index 00000000..2c17e281 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.InMemory.Models; +using Api.Data.InMemory.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.InMemory.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/InMemoryDbContext.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/InMemoryDbContext.cs new file mode 100644 index 00000000..661424d5 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Data/InMemoryDbContext.cs @@ -0,0 +1,11 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; +using Nano.Eventing.Abstractions; + +namespace Api.Data.InMemory.Data; + +/// +public class InMemoryDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs new file mode 100644 index 00000000..de1b56ff --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.InMemory.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.InMemory.Data.Mappings; + +/// +/// Example Creatable And Updatable Mapping. +/// +public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableMapping.cs new file mode 100644 index 00000000..cec926ea --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleCreatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.InMemory.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.InMemory.Data.Mappings; + +/// +/// Example Creatable Mapping. +/// +public class ExampleCreatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleDeletableMapping.cs new file mode 100644 index 00000000..864e4dbf --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleDeletableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.InMemory.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.InMemory.Data.Mappings; + +/// +/// Example Deletable Mapping. +/// +public class ExampleDeletableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..11696401 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.InMemory.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.InMemory.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleUpdatableMapping.cs new file mode 100644 index 00000000..67221228 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Data/Mappings/ExampleUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.InMemory.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.InMemory.Data.Mappings; + +/// +/// Example Updatable Mapping. +/// +public class ExampleUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local b/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local new file mode 100644 index 00000000..a61be1c9 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.InMemory.dll"] \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/Program.cs b/Api.Data.InMemory/Api.Data.InMemory/Program.cs new file mode 100644 index 00000000..d003f412 --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.InMemory.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.InMemory; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.InMemory/Api.Data.InMemory/Properties/InternalsVisibleTo.cs b/Api.Data.InMemory/Api.Data.InMemory/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..e3a05a6c --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.InMemory")] \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Development.json b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Production.json b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/appsettings.Staging.json b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.InMemory/Api.Data.InMemory/appsettings.json b/Api.Data.InMemory/Api.Data.InMemory/appsettings.json new file mode 100644 index 00000000..55288f2b --- /dev/null +++ b/Api.Data.InMemory/Api.Data.InMemory/appsettings.json @@ -0,0 +1,37 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Documentation": { + "Name": "Application" + } + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": "nanoDb", + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } +} \ No newline at end of file diff --git a/Api.Data.InMemory/Dockerfile b/Api.Data.InMemory/Dockerfile new file mode 100644 index 00000000..ff662879 --- /dev/null +++ b/Api.Data.InMemory/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.InMemory.dll"] \ No newline at end of file diff --git a/Api.Data.InMemory/LICENSE b/Api.Data.InMemory/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.InMemory/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.InMemory/README.md b/Api.Data.InMemory/README.md new file mode 100644 index 00000000..bb283b02 --- /dev/null +++ b/Api.Data.InMemory/README.md @@ -0,0 +1,78 @@ +# Api.Data.InMemory + +> _Nano API application with in-memory data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from +the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings), +and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context). As the in-memory data provider doesn't use migrations there is no need +to implement the `BaseDbContextFactory`. + +Additionally, the example shows how Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories) works along with the corresponding +entity controllers. For more information on controllers and how they are connected with entity models, see [Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#controllers). + +Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed +here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing +the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. + +> 📖 Learn more about **[Nano.Data.InMemory](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.InMemory/README#nanodatainmemory)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": "nanoDb", + "Repository": { + "UseAutoSave": false, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null +} +``` diff --git a/Api.Data.InMemory/icon.png b/Api.Data.InMemory/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.LazyLoading/.docker/docker-compose.yml b/Api.Data.LazyLoading/.docker/docker-compose.yml new file mode 100644 index 00000000..b31fee18 --- /dev/null +++ b/Api.Data.LazyLoading/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.lazyloading: + image: api.data.lazyloading + hostname: api-data-lazyloading + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.LazyLoading + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.LazyLoading/.dockerignore b/Api.Data.LazyLoading/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.LazyLoading/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.LazyLoading/.github/config/slack.yml b/Api.Data.LazyLoading/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.LazyLoading/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml b/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..2d8de195 --- /dev/null +++ b/Api.Data.LazyLoading/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.LazyLoading + IMAGE_NAME: api.data.lazyloading + SERVICE_NAME: api-data-lazyloading + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.LazyLoading/.gitignore b/Api.Data.LazyLoading/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.LazyLoading/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.LazyLoading/.kubernetes/auth-sql-secret.yaml b/Api.Data.LazyLoading/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.LazyLoading/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.LazyLoading/.kubernetes/autoscaler.yaml b/Api.Data.LazyLoading/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.LazyLoading/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.LazyLoading/.kubernetes/configmap.yaml b/Api.Data.LazyLoading/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.LazyLoading/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.LazyLoading/.kubernetes/deployment.yaml b/Api.Data.LazyLoading/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.LazyLoading/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.LazyLoading/.kubernetes/service.yaml b/Api.Data.LazyLoading/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.LazyLoading/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Properties/DoNotParallelize.cs b/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Tests.Api.Data.LazyLoading.csproj b/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Tests.Api.Data.LazyLoading.csproj new file mode 100644 index 00000000..451cc28d --- /dev/null +++ b/Api.Data.LazyLoading/.tests/Tests.Api.Data.LazyLoading/Tests.Api.Data.LazyLoading.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Api.Data.LazyLoading.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..9882acfe --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.LazyLoading.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Example.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Example.cs new file mode 100644 index 00000000..d3af35ef --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/Example.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Nano.Data.Abstractions.Annotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.LazyLoading.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; + + /// + /// Relations. + /// + public virtual ICollection Relations { get; set; } = []; + + /// + /// Relations. + /// + [Include] + public virtual ICollection IncludedRelations { get; set; } = []; +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelation.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelation.cs new file mode 100644 index 00000000..4a900e8b --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelation.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.LazyLoading.Models; + +/// +/// Example Relation. +/// +public class ExampleRelation : BaseEntity +{ + /// + /// Example. + /// + [Required] + public virtual Guid ExampleId { get; set; } + + /// + /// Example. + /// + public virtual Example? Example { get; set; } = null!; + + /// + /// Text. + /// + public virtual string? Text { get; set; } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelationIncluded.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelationIncluded.cs new file mode 100644 index 00000000..521bd1ff --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading.Models/ExampleRelationIncluded.cs @@ -0,0 +1,27 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.LazyLoading.Models; + +/// +/// Example Relation Included. +/// +public class ExampleRelationIncluded : BaseEntity +{ + /// + /// Example. + /// + [Required] + public virtual Guid ExampleId { get; set; } + + /// + /// Example. + /// + public virtual Example? Example { get; set; } = null!; + + /// + /// Text. + /// + public virtual string? Text { get; set; } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading.sln b/Api.Data.LazyLoading/Api.Data.LazyLoading.sln new file mode 100644 index 00000000..4b14dc83 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.LazyLoading.Models", "Api.Data.LazyLoading.Models\Api.Data.LazyLoading.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.LazyLoading", "Api.Data.LazyLoading\Api.Data.LazyLoading.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.LazyLoading", ".tests\Tests.Api.Data.LazyLoading\Tests.Api.Data.LazyLoading.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Api.Data.LazyLoading.csproj b/Api.Data.LazyLoading/Api.Data.LazyLoading/Api.Data.LazyLoading.csproj new file mode 100644 index 00000000..6b91d373 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Api.Data.LazyLoading.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Controllers/ExamplesController.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Controllers/ExamplesController.cs new file mode 100644 index 00000000..f3a81a91 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Controllers/ExamplesController.cs @@ -0,0 +1,45 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.Data.LazyLoading.Models; +using Api.Data.LazyLoading.Models.Criterias; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.LazyLoading.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository) +{ + /// + /// Lazy Loading Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("lazy-loading")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LazyLoadingAsync(CancellationToken cancellationToken = default) + { + var example = await this.Repository + .GetFirstAsync(x => true, cancellationToken); + + if (example == null) + { + return this.NotFound(); + } + + var lazyloadedRelations = example.Relations; + var lazyloadedIncludedRelations = example.IncludedRelations; + + return this.Ok(example); + } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleMapping.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..0c49d1c8 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,36 @@ +using System; +using Api.Data.LazyLoading.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.LazyLoading.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + + builder + .HasMany(x => x.Relations) + .WithOne(x => x.Example) + .IsRequired(); + + builder + .HasMany(x => x.IncludedRelations) + .WithOne(x => x.Example) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationIncludedMapping.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationIncludedMapping.cs new file mode 100644 index 00000000..fc03feb5 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationIncludedMapping.cs @@ -0,0 +1,28 @@ +using System; +using Api.Data.LazyLoading.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.LazyLoading.Data.Mappings; + +/// +/// Example Relation Mapping. +/// +public class ExampleRelationIncludedMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .HasOne(x => x.Example) + .WithMany(x => x.IncludedRelations) + .IsRequired(); + + builder + .Property(x => x.Text); + } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationMapping.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationMapping.cs new file mode 100644 index 00000000..7fbc34e4 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/Mappings/ExampleRelationMapping.cs @@ -0,0 +1,28 @@ +using System; +using Api.Data.LazyLoading.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.LazyLoading.Data.Mappings; + +/// +/// Example Relation Mapping. +/// +public class ExampleRelationMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .HasOne(x => x.Example) + .WithMany(x => x.Relations) + .IsRequired(); + + builder + .Property(x => x.Text); + } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContext.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContext.cs new file mode 100644 index 00000000..3874f906 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.LazyLoading.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContextFactory.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..d68db571 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.LazyLoading.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local b/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local new file mode 100644 index 00000000..ad93161d --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.LazyLoading.dll"] \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.Designer.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.Designer.cs new file mode 100644 index 00000000..02d6ffb2 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.Designer.cs @@ -0,0 +1,748 @@ +// +using System; +using Api.Data.LazyLoading.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.LazyLoading.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415143541_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("ExampleId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Text") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("ExampleId"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleRelation"); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelationIncluded", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("ExampleId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Text") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("ExampleId"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleRelationIncluded"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelation", b => + { + b.HasOne("Api.Data.LazyLoading.Models.Example", "Example") + .WithMany("Relations") + .HasForeignKey("ExampleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Example"); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelationIncluded", b => + { + b.HasOne("Api.Data.LazyLoading.Models.Example", "Example") + .WithMany("IncludedRelations") + .HasForeignKey("ExampleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.Example", b => + { + b.Navigation("IncludedRelations"); + + b.Navigation("Relations"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.cs new file mode 100644 index 00000000..efafb878 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/20260415143541_Initial.cs @@ -0,0 +1,685 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.LazyLoading.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleRelation", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ExampleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Text = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleRelation", x => x.Id); + table.ForeignKey( + name: "FK_ExampleRelation_Example_ExampleId", + column: x => x.ExampleId, + principalTable: "Example", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleRelationIncluded", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ExampleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Text = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleRelationIncluded", x => x.Id); + table.ForeignKey( + name: "FK_ExampleRelationIncluded_Example_ExampleId", + column: x => x.ExampleId, + principalTable: "Example", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleRelation_CreatedAt", + table: "ExampleRelation", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleRelation_ExampleId", + table: "ExampleRelation", + column: "ExampleId"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleRelation_IsDeleted", + table: "ExampleRelation", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleRelationIncluded_CreatedAt", + table: "ExampleRelationIncluded", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleRelationIncluded_ExampleId", + table: "ExampleRelationIncluded", + column: "ExampleId"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleRelationIncluded_IsDeleted", + table: "ExampleRelationIncluded", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "ExampleRelation"); + + migrationBuilder.DropTable( + name: "ExampleRelationIncluded"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..115bdf5b --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,745 @@ +// +using System; +using Api.Data.LazyLoading.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.LazyLoading.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("ExampleId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Text") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("ExampleId"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleRelation"); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelationIncluded", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("ExampleId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Text") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("ExampleId"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleRelationIncluded"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelation", b => + { + b.HasOne("Api.Data.LazyLoading.Models.Example", "Example") + .WithMany("Relations") + .HasForeignKey("ExampleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Example"); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.ExampleRelationIncluded", b => + { + b.HasOne("Api.Data.LazyLoading.Models.Example", "Example") + .WithMany("IncludedRelations") + .HasForeignKey("ExampleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.LazyLoading.Models.Example", b => + { + b.Navigation("IncludedRelations"); + + b.Navigation("Relations"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Program.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Program.cs new file mode 100644 index 00000000..296b2db6 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.LazyLoading.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/Properties/InternalsVisibleTo.cs b/Api.Data.LazyLoading/Api.Data.LazyLoading/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..3e44d1d2 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.LazyLoading")] \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Production.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Staging.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.json b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.json new file mode 100644 index 00000000..73c4d65a --- /dev/null +++ b/Api.Data.LazyLoading/Api.Data.LazyLoading/appsettings.json @@ -0,0 +1,17 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null, + "UseLazyLoading": true + } +} \ No newline at end of file diff --git a/Api.Data.LazyLoading/Dockerfile b/Api.Data.LazyLoading/Dockerfile new file mode 100644 index 00000000..14732284 --- /dev/null +++ b/Api.Data.LazyLoading/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.LazyLoading.dll"] \ No newline at end of file diff --git a/Api.Data.LazyLoading/LICENSE b/Api.Data.LazyLoading/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.LazyLoading/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.LazyLoading/README.md b/Api.Data.LazyLoading/README.md new file mode 100644 index 00000000..6cb45b2f --- /dev/null +++ b/Api.Data.LazyLoading/README.md @@ -0,0 +1,33 @@ +# Api.Data.LazyLoading + +> _Nano API application with data lazy loading._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to +demonstrate repository autosave. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. + +Once the object graph is created, notice that only `IncludedRelations` appears in the response. Although `Relations` is lazy-loaded in the code, it is not included +because it lacks the `Include` annotation. + +> 📖 Learn more about **[Nano Data Lazy Loading](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#lazy-loading)**. + +## Configuration +```json +"Data": { + "UseLazyLoading": true +} +``` diff --git a/Api.Data.LazyLoading/icon.png b/Api.Data.LazyLoading/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Collation/.docker/docker-compose.yml b/Api.Data.MySql.Collation/.docker/docker-compose.yml new file mode 100644 index 00000000..edbf3b37 --- /dev/null +++ b/Api.Data.MySql.Collation/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.mysql.collation: + image: api.data.mysql.collation + hostname: api-data-mysql-collation + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.MySql.Collation + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.MySql.Collation/.dockerignore b/Api.Data.MySql.Collation/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.MySql.Collation/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.MySql.Collation/.github/config/slack.yml b/Api.Data.MySql.Collation/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.MySql.Collation/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..530f1d65 --- /dev/null +++ b/Api.Data.MySql.Collation/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.MySql.Collation + IMAGE_NAME: api.data.mysql.collation + SERVICE_NAME: api-data-mysql-collation + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/.gitignore b/Api.Data.MySql.Collation/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.MySql.Collation/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.Collation/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.Collation/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.MySql.Collation/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.MySql.Collation/.kubernetes/autoscaler.yaml b/Api.Data.MySql.Collation/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.MySql.Collation/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.Collation/.kubernetes/configmap.yaml b/Api.Data.MySql.Collation/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.MySql.Collation/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.Collation/.kubernetes/deployment.yaml b/Api.Data.MySql.Collation/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.MySql.Collation/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.MySql.Collation/.kubernetes/service.yaml b/Api.Data.MySql.Collation/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.MySql.Collation/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Properties/DoNotParallelize.cs b/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Tests.Api.Data.MySql.Collation.csproj b/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Tests.Api.Data.MySql.Collation.csproj new file mode 100644 index 00000000..af89191c --- /dev/null +++ b/Api.Data.MySql.Collation/.tests/Tests.Api.Data.MySql.Collation/Tests.Api.Data.MySql.Collation.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Api.Data.MySql.Collation.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..9020b2bb --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.MySql.Collation.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Example.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Example.cs new file mode 100644 index 00000000..8044c6eb --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Collation.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation.sln b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.sln new file mode 100644 index 00000000..932fcc07 --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Collation.Models", "Api.Data.MySql.Collation.Models\Api.Data.MySql.Collation.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Collation", "Api.Data.MySql.Collation\Api.Data.MySql.Collation.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.Collation", ".tests\Tests.Api.Data.MySql.Collation\Tests.Api.Data.MySql.Collation.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Api.Data.MySql.Collation.csproj b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Api.Data.MySql.Collation.csproj new file mode 100644 index 00000000..36f2c8c0 --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Api.Data.MySql.Collation.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Controllers/ExamplesController.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Controllers/ExamplesController.cs new file mode 100644 index 00000000..22947a2c --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Collation.Models; +using Api.Data.MySql.Collation.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Collation.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..670874df --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.MySql.Collation.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Collation.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContext.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContext.cs new file mode 100644 index 00000000..1acf9a1e --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContext.cs @@ -0,0 +1,11 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; +using Nano.Eventing.Abstractions; + +namespace Api.Data.MySql.Collation.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..55b48f03 --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.MySql.Collation.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local new file mode 100644 index 00000000..f2716c69 --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.MySql.Collation.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.Designer.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.Designer.cs new file mode 100644 index 00000000..dccc1a17 --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.Designer.cs @@ -0,0 +1,651 @@ +// +using System; +using Api.Data.MySql.Collation.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Collation.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415143932_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Collation.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.cs new file mode 100644 index 00000000..4cf37a7a --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/20260415143932_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.MySql.Collation.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..11b521cf --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,648 @@ +// +using System; +using Api.Data.MySql.Collation.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Collation.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Collation.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Program.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Program.cs new file mode 100644 index 00000000..ff90c81e --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.MySql.Collation.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..32060844 --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.Collation")] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Production.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Staging.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json new file mode 100644 index 00000000..d43a066a --- /dev/null +++ b/Api.Data.MySql.Collation/Api.Data.MySql.Collation/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Collation/Dockerfile b/Api.Data.MySql.Collation/Dockerfile new file mode 100644 index 00000000..0d24bca4 --- /dev/null +++ b/Api.Data.MySql.Collation/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.MySql.Collation.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Collation/LICENSE b/Api.Data.MySql.Collation/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.MySql.Collation/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.Collation/README.md b/Api.Data.MySql.Collation/README.md new file mode 100644 index 00000000..9ae5aa06 --- /dev/null +++ b/Api.Data.MySql.Collation/README.md @@ -0,0 +1,37 @@ +# Api.Data.MySql.Collation + +> _Nano API application with mysql collation data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been +simplified to showcase setting mysql default collation; full controllers are unnecessary. + +This example demonstrates setting the `DefaultCollation` in the `Data` section of the configuration. Notice that querying `Example.Name` with +a case-insensitive collation returns results regardless of letter casing. + +⚠️ Note: Changing this setting affects only new migrations and will not modify existing tables or columns. + +> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql/README.md#nanodatamysql)**. + +## Configuration +The collation is set in `appsettings`. + +```json +"Data": { + "DefaultCollation": "utf8mb4_general_ci" +} +``` \ No newline at end of file diff --git a/Api.Data.MySql.Collation/icon.png b/Api.Data.MySql.Collation/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/.docker/docker-compose.yml b/Api.Data.MySql.Mappings/.docker/docker-compose.yml new file mode 100644 index 00000000..7fe5c2e8 --- /dev/null +++ b/Api.Data.MySql.Mappings/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.mysql.mappings: + image: api.data.mysql.mappings + hostname: api-data-mysql-mappings + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.MySql.Mappings + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.MySql.Mappings/.dockerignore b/Api.Data.MySql.Mappings/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.MySql.Mappings/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.MySql.Mappings/.github/config/slack.yml b/Api.Data.MySql.Mappings/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.MySql.Mappings/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..4e0460d8 --- /dev/null +++ b/Api.Data.MySql.Mappings/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.MySql.Mappings + IMAGE_NAME: api.data.mysql.mappings + SERVICE_NAME: api-data-mysql-mappings + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/.gitignore b/Api.Data.MySql.Mappings/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.MySql.Mappings/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.Mappings/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.MySql.Mappings/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.MySql.Mappings/.kubernetes/autoscaler.yaml b/Api.Data.MySql.Mappings/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.MySql.Mappings/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml b/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.MySql.Mappings/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml b/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.MySql.Mappings/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.MySql.Mappings/.kubernetes/service.yaml b/Api.Data.MySql.Mappings/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.MySql.Mappings/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Properties/DoNotParallelize.cs b/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Tests.Api.Data.MySql.Mappings.csproj b/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Tests.Api.Data.MySql.Mappings.csproj new file mode 100644 index 00000000..20af7932 --- /dev/null +++ b/Api.Data.MySql.Mappings/.tests/Tests.Api.Data.MySql.Mappings/Tests.Api.Data.MySql.Mappings.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Api.Data.MySql.Mappings.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleNormalizedQueryCriteria.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleNormalizedQueryCriteria.cs new file mode 100644 index 00000000..d741ec05 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleNormalizedQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.MySql.Mappings.Models.Criterias; + +/// +public class ExampleNormalizedQueryCriteria : BaseQueryCriteria +{ + /// + /// Full Name. + /// + public virtual string? FullName { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.FullName)) + { + expression + .StartsWith(nameof(ExampleNormalized.FullNameNormalized), this.FullName.ToUpper()); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..856b17b2 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.MySql.Mappings.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleJson.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleJson.cs new file mode 100644 index 00000000..9fdb849e --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleJson.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Api.Data.MySql.Mappings.Models.Types; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Mappings.Models; + +/// +/// Example Json. +/// +public class ExampleJson : BaseEntity +{ + /// + /// Profile As Json. + /// + [Required] + public virtual Profile ProfileAsJson { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleNormalized.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleNormalized.cs new file mode 100644 index 00000000..7c63aefe --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleNormalized.cs @@ -0,0 +1,65 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Mappings.Models; + +/// +/// Example Normalized. +/// +public class ExampleNormalized : BaseEntity +{ + private string firstName = null!; + private string lastName = null!; + + /// + /// First Name. + /// + [Required] + [MaxLength(128)] + public virtual string FirstName + { + get => this.firstName; + set + { + this.firstName = value; + this.FullName = $"{this.firstName} {this.lastName}"; + } + } + + /// + /// Last Name. + /// + [Required] + [MaxLength(128)] + public virtual string LastName + { + get => this.lastName; + set + { + this.lastName = value; + this.FullName = $"{this.firstName} {this.lastName}"; + } + } + + /// + /// Full Name. + /// + [Required] + [MaxLength(256)] + public virtual string FullName + { + get; + set + { + field = value; + this.FullNameNormalized = value.ToUpper(); + } + } = null!; + + /// + /// Full Name Normalized. + /// + [Required] + [MaxLength(256)] + public virtual string FullNameNormalized { get; internal set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleOwned.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleOwned.cs new file mode 100644 index 00000000..288b1786 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/ExampleOwned.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; +using Api.Data.MySql.Mappings.Models.Types; + +namespace Api.Data.MySql.Mappings.Models; + +/// +/// Example Owned. +/// +public class ExampleOwned : BaseEntity +{ + /// + /// Profile. + /// + [Required] + public virtual Profile Profile { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/Profile.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/Profile.cs new file mode 100644 index 00000000..985cdbfd --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/Profile.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations; + +namespace Api.Data.MySql.Mappings.Models.Types; + +/// +/// Profile. +/// +public class Profile +{ + /// + /// Text. + /// + public virtual string? Text { get; set; } + + /// + /// Picture. + /// + public virtual ProfilePicture? Picture { get; set; } + + /// + /// Settings. + /// + [Required] + public virtual ProfileSettings Settings { get; set; } = new(); +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfilePicture.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfilePicture.cs new file mode 100644 index 00000000..026650b9 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfilePicture.cs @@ -0,0 +1,19 @@ +using System; + +namespace Api.Data.MySql.Mappings.Models.Types; + +/// +/// Profile Picture. +/// +public class ProfilePicture +{ + /// + /// Text. + /// + public virtual Guid Id { get; set; } + + /// + /// Text. + /// + public virtual string? Path { get; set; } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfileSettings.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfileSettings.cs new file mode 100644 index 00000000..23548886 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.Models/Types/ProfileSettings.cs @@ -0,0 +1,24 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Api.Data.MySql.Mappings.Models.Types; + +/// +/// Profile Settings. +/// +public class ProfileSettings +{ + /// + /// Use Dark Mode. + /// + [Required] + [DefaultValue(false)] + public virtual bool UseDarkMode { get; set; } = false; + + /// + /// Hide Profile Name. + /// + [Required] + [DefaultValue(false)] + public virtual bool HideProfileName { get; set; } = false; +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.sln b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.sln new file mode 100644 index 00000000..30021fd7 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Mappings.Models", "Api.Data.MySql.Mappings.Models\Api.Data.MySql.Mappings.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Mappings", "Api.Data.MySql.Mappings\Api.Data.MySql.Mappings.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.Mappings", ".tests\Tests.Api.Data.MySql.Mappings\Tests.Api.Data.MySql.Mappings.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.csproj b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.csproj new file mode 100644 index 00000000..aaf663e6 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleJsonController.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleJsonController.cs new file mode 100644 index 00000000..60bef305 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleJsonController.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; +using Api.Data.MySql.Mappings.Models; +using Api.Data.MySql.Mappings.Models.Criterias; + +namespace Api.Data.MySql.Mappings.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleJsonsController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleNormalizedController.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleNormalizedController.cs new file mode 100644 index 00000000..46e2bcbf --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleNormalizedController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Mappings.Models; +using Api.Data.MySql.Mappings.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Mappings.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleNormalizedsController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleOwnedController.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleOwnedController.cs new file mode 100644 index 00000000..8ff3ee89 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Controllers/ExampleOwnedController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Mappings.Models; +using Api.Data.MySql.Mappings.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Mappings.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleOwnedsController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleJsonMapping.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleJsonMapping.cs new file mode 100644 index 00000000..785296a2 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleJsonMapping.cs @@ -0,0 +1,28 @@ +using System; +using Api.Data.MySql.Mappings.Models; +using Api.Data.MySql.Mappings.Models.Types; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using Newtonsoft.Json; + +namespace Api.Data.MySql.Mappings.Data.Mappings; + +/// +/// Example Json Mapping. +/// +public class ExampleJsonMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.ProfileAsJson) + .HasConversion( + x => JsonConvert.SerializeObject(x), + x => JsonConvert.DeserializeObject(x)!); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleNormalizedMapping.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleNormalizedMapping.cs new file mode 100644 index 00000000..aa1e45bb --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleNormalizedMapping.cs @@ -0,0 +1,44 @@ +using Api.Data.MySql.Mappings.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using System; + +namespace Api.Data.MySql.Mappings.Data.Mappings; + +/// +/// Example Normalized Mapping. +/// +public class ExampleNormalizedMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.FirstName) + .HasMaxLength(128) + .IsRequired(); + + builder + .Property(x => x.LastName) + .HasMaxLength(128) + .IsRequired(); + + builder + .Property(x => x.FullName) + .HasMaxLength(256) + .IsRequired(); + + builder + .Property(x => x.FullNameNormalized) + .HasMaxLength(256) + .IsRequired(); + + builder + .HasIndex(x => x.FullNameNormalized) + .IsUnique(); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleOwnedMapping.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleOwnedMapping.cs new file mode 100644 index 00000000..b691511d --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/ExampleOwnedMapping.cs @@ -0,0 +1,24 @@ +using System; +using Api.Data.MySql.Mappings.Data.Mappings.Extensions; +using Api.Data.MySql.Mappings.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Mappings.Data.Mappings; + +/// +/// Example Owned Mapping. +/// +public class ExampleOwnedMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .MapType(x => x.Profile); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/EntityTypeBuilderExtensions.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/EntityTypeBuilderExtensions.cs new file mode 100644 index 00000000..3dacc445 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/EntityTypeBuilderExtensions.cs @@ -0,0 +1,41 @@ +using System; +using System.Linq.Expressions; +using Api.Data.MySql.Mappings.Models.Types; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Abstractions.Models.Abstractions; + +namespace Api.Data.MySql.Mappings.Data.Mappings.Extensions; + +/// +/// Entity Type Builder Extensions. +/// +public static class EntityTypeBuilderExtensions +{ + /// + /// Maps as owned by the entity. + /// + /// The entity type. + /// The . + /// The property expression. + public static void MapType(this EntityTypeBuilder builder, Expression> expression) + where TEntity : class, IEntity + { + if (builder == null) + throw new ArgumentNullException(nameof(builder)); + + if (expression == null) + throw new ArgumentNullException(nameof(expression)); + + builder + .OwnsOne(expression) + .Property(x => x.Text); + + builder + .OwnsOne(expression) + .MapType(x => x.Picture); + + builder + .OwnsOne(expression) + .MapType(x => x.Settings); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/OwnedNavigationBuilderExtensions.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/OwnedNavigationBuilderExtensions.cs new file mode 100644 index 00000000..65e6a37e --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/Mappings/Extensions/OwnedNavigationBuilderExtensions.cs @@ -0,0 +1,70 @@ +using System; +using System.Linq.Expressions; +using Api.Data.MySql.Mappings.Models.Types; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Api.Data.MySql.Mappings.Data.Mappings.Extensions; + +/// +/// Owned Navigation Builder Extensions. +/// +public static class OwnedNavigationBuilderExtensions +{ + /// + /// Maps for the owned by . + /// + /// The entity type. + /// The related entity type. + /// The . + /// The . + internal static void MapType(this OwnedNavigationBuilder builder, Expression> expression) + where TEntity : class + where TRelatedEntity : class + { + if (builder == null) + throw new ArgumentNullException(nameof(builder)); + + if (expression == null) + throw new ArgumentNullException(nameof(expression)); + + builder + .OwnsOne(expression) + .Property(x => x.Id) + .IsRequired(); + + builder + .OwnsOne(expression) + .Property(x => x.Path); + } + + /// + /// Maps for the owned by . + /// + /// The entity type. + /// The related entity type. + /// The . + /// The . + internal static void MapType(this OwnedNavigationBuilder builder, Expression> expression) + where TEntity : class + where TRelatedEntity : class + { + if (builder == null) + throw new ArgumentNullException(nameof(builder)); + + if (expression == null) + throw new ArgumentNullException(nameof(expression)); + + builder + .OwnsOne(expression) + .Property(x => x.UseDarkMode) + .HasDefaultValue(false) + .IsRequired(); + + builder + .OwnsOne(expression) + .Property(x => x.HideProfileName) + .HasDefaultValue(false) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContext.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContext.cs new file mode 100644 index 00000000..f0d0786b --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.MySql.Mappings.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..719b04a7 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.MySql.Mappings.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local new file mode 100644 index 00000000..88ec81d2 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.MySql.Mappings.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.Designer.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.Designer.cs new file mode 100644 index 00000000..d4f92177 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.Designer.cs @@ -0,0 +1,794 @@ +// +using System; +using Api.Data.MySql.Mappings.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Mappings.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415143933_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleJson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("ProfileAsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleJson"); + }); + + modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleNormalized", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("FullNameNormalized") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("FullNameNormalized") + .IsUnique() + .HasDatabaseName("UX_ExampleNormalized_FullNameNormalized"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNormalized"); + }); + + modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleOwned", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleOwned"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleOwned", b => + { + b.OwnsOne("Api.Data.MySql.Mappings.Models.Types.Profile", "Profile", b1 => + { + b1.Property("ExampleOwnedId") + .HasColumnType("char(36)"); + + b1.Property("Text") + .HasColumnType("longtext"); + + b1.HasKey("ExampleOwnedId"); + + b1.ToTable("ExampleOwned"); + + b1.WithOwner() + .HasForeignKey("ExampleOwnedId"); + + b1.OwnsOne("Api.Data.MySql.Mappings.Models.Types.ProfilePicture", "Picture", b2 => + { + b2.Property("ProfileExampleOwnedId") + .HasColumnType("char(36)"); + + b2.Property("Id") + .HasColumnType("char(36)"); + + b2.Property("Path") + .HasColumnType("longtext"); + + b2.HasKey("ProfileExampleOwnedId"); + + b2.ToTable("ExampleOwned"); + + b2.WithOwner() + .HasForeignKey("ProfileExampleOwnedId"); + }); + + b1.OwnsOne("Api.Data.MySql.Mappings.Models.Types.ProfileSettings", "Settings", b2 => + { + b2.Property("ProfileExampleOwnedId") + .HasColumnType("char(36)"); + + b2.Property("HideProfileName") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b2.Property("UseDarkMode") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b2.HasKey("ProfileExampleOwnedId"); + + b2.ToTable("ExampleOwned"); + + b2.WithOwner() + .HasForeignKey("ProfileExampleOwnedId"); + }); + + b1.Navigation("Picture"); + + b1.Navigation("Settings") + .IsRequired(); + }); + + b.Navigation("Profile") + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.cs new file mode 100644 index 00000000..ad41219f --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/20260415143933_Initial.cs @@ -0,0 +1,673 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.MySql.Mappings.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleJson", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ProfileAsJson = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleJson", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleNormalized", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + FirstName = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + LastName = table.Column(type: "varchar(128)", maxLength: 128, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FullName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + FullNameNormalized = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleNormalized", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleOwned", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Profile_Text = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Profile_Picture_Id = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + Profile_Picture_Path = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Profile_Settings_UseDarkMode = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), + Profile_Settings_HideProfileName = table.Column(type: "tinyint(1)", nullable: false, defaultValue: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleOwned", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleJson_CreatedAt", + table: "ExampleJson", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleJson_IsDeleted", + table: "ExampleJson", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNormalized_CreatedAt", + table: "ExampleNormalized", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleNormalized_IsDeleted", + table: "ExampleNormalized", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "UX_ExampleNormalized_FullNameNormalized", + table: "ExampleNormalized", + column: "FullNameNormalized", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ExampleOwned_CreatedAt", + table: "ExampleOwned", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleOwned_IsDeleted", + table: "ExampleOwned", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "ExampleJson"); + + migrationBuilder.DropTable( + name: "ExampleNormalized"); + + migrationBuilder.DropTable( + name: "ExampleOwned"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..fbb4bbd7 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,791 @@ +// +using System; +using Api.Data.MySql.Mappings.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Mappings.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleJson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("ProfileAsJson") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleJson"); + }); + + modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleNormalized", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("FirstName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.Property("FullName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("FullNameNormalized") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("LastName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("varchar(128)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("FullNameNormalized") + .IsUnique() + .HasDatabaseName("UX_ExampleNormalized_FullNameNormalized"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleNormalized"); + }); + + modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleOwned", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleOwned"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.MySql.Mappings.Models.ExampleOwned", b => + { + b.OwnsOne("Api.Data.MySql.Mappings.Models.Types.Profile", "Profile", b1 => + { + b1.Property("ExampleOwnedId") + .HasColumnType("char(36)"); + + b1.Property("Text") + .HasColumnType("longtext"); + + b1.HasKey("ExampleOwnedId"); + + b1.ToTable("ExampleOwned"); + + b1.WithOwner() + .HasForeignKey("ExampleOwnedId"); + + b1.OwnsOne("Api.Data.MySql.Mappings.Models.Types.ProfilePicture", "Picture", b2 => + { + b2.Property("ProfileExampleOwnedId") + .HasColumnType("char(36)"); + + b2.Property("Id") + .HasColumnType("char(36)"); + + b2.Property("Path") + .HasColumnType("longtext"); + + b2.HasKey("ProfileExampleOwnedId"); + + b2.ToTable("ExampleOwned"); + + b2.WithOwner() + .HasForeignKey("ProfileExampleOwnedId"); + }); + + b1.OwnsOne("Api.Data.MySql.Mappings.Models.Types.ProfileSettings", "Settings", b2 => + { + b2.Property("ProfileExampleOwnedId") + .HasColumnType("char(36)"); + + b2.Property("HideProfileName") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b2.Property("UseDarkMode") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false); + + b2.HasKey("ProfileExampleOwnedId"); + + b2.ToTable("ExampleOwned"); + + b2.WithOwner() + .HasForeignKey("ProfileExampleOwnedId"); + }); + + b1.Navigation("Picture"); + + b1.Navigation("Settings") + .IsRequired(); + }); + + b.Navigation("Profile") + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Program.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Program.cs new file mode 100644 index 00000000..f97f43b4 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.MySql.Mappings.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..36bba3c1 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.Mappings")] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Production.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Staging.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.json b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.json new file mode 100644 index 00000000..d43a066a --- /dev/null +++ b/Api.Data.MySql.Mappings/Api.Data.MySql.Mappings/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/Dockerfile b/Api.Data.MySql.Mappings/Dockerfile new file mode 100644 index 00000000..bd6047d9 --- /dev/null +++ b/Api.Data.MySql.Mappings/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.MySql.Mappings.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/LICENSE b/Api.Data.MySql.Mappings/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.MySql.Mappings/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.Mappings/README.md b/Api.Data.MySql.Mappings/README.md new file mode 100644 index 00000000..90542d3d --- /dev/null +++ b/Api.Data.MySql.Mappings/README.md @@ -0,0 +1,30 @@ +# Api.Data.MySql.Mappings + +> _Nano API application with mysql advanced data mappings._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been +simplified to showcase setting mysql views; full controllers are unnecessary. + +Three new entity models have been added, each demonstrating different types of advanced mappings. The first, `ExampleJson`, shows how to store a complex object in a +text column: it is serialized when added or updated in the database and deserialized when retrieved, mapping back into the complex object. The second, `ExampleOwned`, +also has a `Profile` reference like `ExampleJson`, but the complex object is stored as **owned entities** within the same table. The third, `ExampleNormalized`, +demonstrates property normalization for querying: `FirstName` and `LastName` are concatenated into `FullName`, and an uppercase version, `FullNameNormalized`, is +used for case-insensitive searches, with the LINQ query calling `.ToUpper()` on the search value to match the normalized property efficiently. + +Last, a unique index has also been added to `Example.NameNormalized`. Observe how Nano renames the index prefixing with 'UX_'. + +> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql/README.md#nanodatamysql)**. diff --git a/Api.Data.MySql.Mappings/icon.png b/Api.Data.MySql.Mappings/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/.docker/docker-compose.yml b/Api.Data.MySql.Spatial/.docker/docker-compose.yml new file mode 100644 index 00000000..309848ae --- /dev/null +++ b/Api.Data.MySql.Spatial/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.mysql.spatial: + image: api.data.mysql.spatial + hostname: api-data-mysql-spatial + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.MySql.Spatial + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.MySql.Spatial/.dockerignore b/Api.Data.MySql.Spatial/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.MySql.Spatial/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.MySql.Spatial/.github/config/slack.yml b/Api.Data.MySql.Spatial/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.MySql.Spatial/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..956aec17 --- /dev/null +++ b/Api.Data.MySql.Spatial/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.MySql.Spatial + IMAGE_NAME: api.data.mysql.spatial + SERVICE_NAME: api-data-mysql-spatial + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/.gitignore b/Api.Data.MySql.Spatial/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.MySql.Spatial/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.Spatial/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.MySql.Spatial/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.MySql.Spatial/.kubernetes/autoscaler.yaml b/Api.Data.MySql.Spatial/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.MySql.Spatial/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml b/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.MySql.Spatial/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml b/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.MySql.Spatial/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.MySql.Spatial/.kubernetes/service.yaml b/Api.Data.MySql.Spatial/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.MySql.Spatial/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Properties/DoNotParallelize.cs b/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Tests.Api.Data.MySql.Spatial.csproj b/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Tests.Api.Data.MySql.Spatial.csproj new file mode 100644 index 00000000..8ea090d1 --- /dev/null +++ b/Api.Data.MySql.Spatial/.tests/Tests.Api.Data.MySql.Spatial/Tests.Api.Data.MySql.Spatial.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Api.Data.MySql.Spatial.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..198440ff --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,46 @@ +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; +using NetTopologySuite.Geometries; +using System.Collections.Generic; +using System.Linq; + +namespace Api.Data.MySql.Spatial.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Point. + /// + public virtual Point? Point { get; set; } + + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (this.Point != null) + { + expression + .IsWithinDistance(nameof(Example.Point), this.Point, 10000); + } + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Example.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Example.cs new file mode 100644 index 00000000..2e16f95a --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.Models/Example.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; +using NetTopologySuite.Geometries; + +namespace Api.Data.MySql.Spatial.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Point. + /// + [Required] + public virtual Point Point { get; set; } = null!; + + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.sln b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.sln new file mode 100644 index 00000000..c213021a --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Spatial.Models", "Api.Data.MySql.Spatial.Models\Api.Data.MySql.Spatial.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Spatial", "Api.Data.MySql.Spatial\Api.Data.MySql.Spatial.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.Spatial", ".tests\Tests.Api.Data.MySql.Spatial\Tests.Api.Data.MySql.Spatial.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.csproj b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.csproj new file mode 100644 index 00000000..cab843f5 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Controllers/ExamplesController.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Controllers/ExamplesController.cs new file mode 100644 index 00000000..55588adc --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Spatial.Models; +using Api.Data.MySql.Spatial.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Spatial.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..657d2a66 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,39 @@ +using System; +using Api.Data.MySql.Spatial.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using NetTopologySuite.Geometries; + +namespace Api.Data.MySql.Spatial.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Point) + .HasColumnType("POINT") + .HasSpatialReferenceSystem(4326) + .HasDefaultValue(new Point(0, 0) { SRID = 4326 }) + .IsRequired(); + + builder + .HasIndex(x => x.Point) + .IsSpatial(); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContext.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContext.cs new file mode 100644 index 00000000..577d6fc9 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.MySql.Spatial.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..153aed1c --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.MySql.Spatial.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local new file mode 100644 index 00000000..33203aa6 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.MySql.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.Designer.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.Designer.cs new file mode 100644 index 00000000..57095ab5 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.Designer.cs @@ -0,0 +1,662 @@ +// +using System; +using Api.Data.MySql.Spatial.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; + +#nullable disable + +namespace Api.Data.MySql.Spatial.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415143931_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Spatial.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Point") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("POINT") + .HasDefaultValue((NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")) + .HasAnnotation("MySql:SpatialReferenceSystemId", 4326); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.HasIndex("Point") + .HasAnnotation("MySql:SpatialIndex", true); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.cs new file mode 100644 index 00000000..b036340c --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/20260415143931_Initial.cs @@ -0,0 +1,610 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using NetTopologySuite.Geometries; + +#nullable disable + +namespace Api.Data.MySql.Spatial.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Point = table.Column(type: "point", nullable: false, defaultValue: (NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")) + .Annotation("MySql:SpatialReferenceSystemId", 4326), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Point", + table: "Example", + column: "Point") + .Annotation("MySql:SpatialIndex", true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..0ba27d24 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,659 @@ +// +using System; +using Api.Data.MySql.Spatial.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; + +#nullable disable + +namespace Api.Data.MySql.Spatial.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Spatial.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("Point") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("POINT") + .HasDefaultValue((NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")) + .HasAnnotation("MySql:SpatialReferenceSystemId", 4326); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.HasIndex("Point") + .HasAnnotation("MySql:SpatialIndex", true); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Program.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Program.cs new file mode 100644 index 00000000..cec8e9f2 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.MySql.Spatial.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..d42997e9 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.Spatial")] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Production.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Staging.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.json b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.json new file mode 100644 index 00000000..d43a066a --- /dev/null +++ b/Api.Data.MySql.Spatial/Api.Data.MySql.Spatial/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/Dockerfile b/Api.Data.MySql.Spatial/Dockerfile new file mode 100644 index 00000000..dce34833 --- /dev/null +++ b/Api.Data.MySql.Spatial/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.MySql.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/LICENSE b/Api.Data.MySql.Spatial/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.MySql.Spatial/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.Spatial/README.md b/Api.Data.MySql.Spatial/README.md new file mode 100644 index 00000000..49b8f797 --- /dev/null +++ b/Api.Data.MySql.Spatial/README.md @@ -0,0 +1,25 @@ +# Api.Data.MySql.Spatial + +> _Nano API application with mysql spatial data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been simplified to +showcase spatial types; full controllers are unnecessary. + +The `Example` entity now includes a `Point` property from `NetTopologySuite`. A query criterion has been added to check whether points are within a 10,000 meter distance. The +entity mappings for this spatial property have also been configured. Otherwise, no other changes were made. + +> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql/README.md#nanodatamysql)**. diff --git a/Api.Data.MySql.Spatial/icon.png b/Api.Data.MySql.Spatial/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml b/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml new file mode 100644 index 00000000..5f01dfb6 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.mysql.storedprocedures: + image: api.data.mysql.storedprocedures + hostname: api-data-mysql-storedprocedures + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.MySql.StoredProcedures + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.MySql.StoredProcedures/.dockerignore b/Api.Data.MySql.StoredProcedures/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.MySql.StoredProcedures/.github/config/slack.yml b/Api.Data.MySql.StoredProcedures/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..d7056b63 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.MySql.StoredProcedures + IMAGE_NAME: api.data.mysql.storedprocedures + SERVICE_NAME: api-data-mysql-storedprocedures + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/.gitignore b/Api.Data.MySql.StoredProcedures/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/autoscaler.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.MySql.StoredProcedures/.kubernetes/service.yaml b/Api.Data.MySql.StoredProcedures/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Properties/DoNotParallelize.cs b/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Tests.Api.Data.MySql.StoredProcedures.csproj b/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Tests.Api.Data.MySql.StoredProcedures.csproj new file mode 100644 index 00000000..ff6e17e9 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/.tests/Tests.Api.Data.MySql.StoredProcedures/Tests.Api.Data.MySql.StoredProcedures.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Api.Data.MySql.StoredProcedures.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Example.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Example.cs new file mode 100644 index 00000000..7117dc91 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/Example.cs @@ -0,0 +1,23 @@ +using Nano.Data.Abstractions.Models; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace Api.Data.MySql.StoredProcedures.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; + + /// + /// Name. + /// + [Required] + [DefaultValue(0)] + public virtual int Counter { get; set; } = 0; +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/ExampleResult.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/ExampleResult.cs new file mode 100644 index 00000000..cb7156c8 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.Models/ExampleResult.cs @@ -0,0 +1,26 @@ +using System; +using Nano.Data.Abstractions.Models; +using Nano.Data.Abstractions.Models.Abstractions; + +namespace Api.Data.MySql.StoredProcedures.Models; + +/// +/// Example Result. +/// +public class ExampleResult : BaseEntityView +{ + /// + /// Id. + /// + public virtual Guid Id { get; set; } + + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; + + /// + /// Counter. + /// + public virtual int Counter { get; set; } = 0; +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.sln b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.sln new file mode 100644 index 00000000..2a061f2a --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.StoredProcedures.Models", "Api.Data.MySql.StoredProcedures.Models\Api.Data.MySql.StoredProcedures.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.StoredProcedures", "Api.Data.MySql.StoredProcedures\Api.Data.MySql.StoredProcedures.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.StoredProcedures", ".tests\Tests.Api.Data.MySql.StoredProcedures\Tests.Api.Data.MySql.StoredProcedures.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.csproj b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.csproj new file mode 100644 index 00000000..9fcba059 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Controllers/ExamplesController.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Controllers/ExamplesController.cs new file mode 100644 index 00000000..f4365bf2 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Controllers/ExamplesController.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.Data.MySql.StoredProcedures.Data.Extensions; + +namespace Api.Data.MySql.StoredProcedures.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository) +{ + /// + /// Stored Procedure Action. + /// + /// The example name. + /// The cancellation token. + /// A result. + /// Success. + [HttpGet] + [Route("stored-procedure")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task StoredProcedureAsync([Required]string name, CancellationToken cancellationToken = default) + { + var result = await this.Repository + .GetExampleResult(name, cancellationToken); + + return this.Ok(result); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Extensions/RepositoryExtensions.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Extensions/RepositoryExtensions.cs new file mode 100644 index 00000000..00df47e9 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Extensions/RepositoryExtensions.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Api.Data.MySql.StoredProcedures.Data.StoredProcedures; +using Api.Data.MySql.StoredProcedures.Models; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.StoredProcedures.Data.Extensions; + +internal static class RepositoryExtensions +{ + internal static async Task GetExampleResult(this IRepository repository, string name, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(repository); + ArgumentNullException.ThrowIfNull(name); + + var dict = new Dictionary + { + { "p_name", name } + }; + + var result = await repository + .ExecuteProcedureAsync(ExampleStoredProcedureDefinition.ExampleCreateOrIncrement, dict, cancellationToken); + + return result; + } +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..10ef53ef --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,32 @@ +using System; +using Api.Data.MySql.StoredProcedures.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.StoredProcedures.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + + builder + .Property(x => x.Counter) + .HasDefaultValue(0) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContext.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContext.cs new file mode 100644 index 00000000..1a70ac2d --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.MySql.StoredProcedures.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..7cb2bf45 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.MySql.StoredProcedures.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/StoredProcedures/StoredProcedures.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/StoredProcedures/StoredProcedures.cs new file mode 100644 index 00000000..aa454296 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Data/StoredProcedures/StoredProcedures.cs @@ -0,0 +1,28 @@ +using Api.Data.MySql.StoredProcedures.Models; + +namespace Api.Data.MySql.StoredProcedures.Data.StoredProcedures; + +internal static class ExampleStoredProcedureDefinition +{ + internal static string ExampleCreateOrIncrement => nameof(ExampleStoredProcedureDefinition.ExampleCreateOrIncrement); + + internal const string SQL = $@" + DROP PROCEDURE IF EXISTS {nameof(ExampleStoredProcedureDefinition.ExampleCreateOrIncrement)}; + + CREATE PROCEDURE {nameof(ExampleStoredProcedureDefinition.ExampleCreateOrIncrement)} ( + IN p_name VARCHAR(255) + ) + BEGIN + DECLARE v_id CHAR(36); + SELECT {nameof(Example.Id)} INTO v_id FROM {nameof(Example)} WHERE {nameof(Example.Name)} = p_name LIMIT 1; + + IF v_id IS NOT NULL THEN + UPDATE {nameof(Example)} SET {nameof(Example.Counter)} = {nameof(Example.Counter)} + 1 WHERE {nameof(Example.Id)} = v_id; + ELSE + SET v_id = UUID(); + INSERT INTO {nameof(Example)} ({nameof(Example.Id)}, {nameof(Example.Name)}, {nameof(Example.Counter)}) VALUES (v_id, p_name, 1); + END IF; + + SELECT {nameof(Example.Id)}, {nameof(Example.Name)}, {nameof(Example.Counter)} FROM {nameof(Example)} WHERE {nameof(Example.Id)} = v_id; + END"; +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local new file mode 100644 index 00000000..e3651ec7 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.MySql.StoredProcedures.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.Designer.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.Designer.cs new file mode 100644 index 00000000..53eebeac --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.Designer.cs @@ -0,0 +1,656 @@ +// +using System; +using Api.Data.MySql.StoredProcedures.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.StoredProcedures.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415151833_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.StoredProcedures.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Counter") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.cs new file mode 100644 index 00000000..f22911dd --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151833_Initial.cs @@ -0,0 +1,602 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.MySql.StoredProcedures.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Counter = table.Column(type: "int", nullable: false, defaultValue: 0), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.Designer.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.Designer.cs new file mode 100644 index 00000000..6f337ac2 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.Designer.cs @@ -0,0 +1,656 @@ +// +using System; +using Api.Data.MySql.StoredProcedures.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.StoredProcedures.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415151941_AddedStoredProcedure")] + partial class AddedStoredProcedure + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.StoredProcedures.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Counter") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.cs new file mode 100644 index 00000000..a57640c5 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/20260415151941_AddedStoredProcedure.cs @@ -0,0 +1,24 @@ +using Api.Data.MySql.StoredProcedures.Data.StoredProcedures; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.MySql.StoredProcedures.Migrations +{ + /// + public partial class AddedStoredProcedure : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder + .Sql(ExampleStoredProcedureDefinition.SQL); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..6f75602c --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,653 @@ +// +using System; +using Api.Data.MySql.StoredProcedures.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.StoredProcedures.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.StoredProcedures.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Counter") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Program.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Program.cs new file mode 100644 index 00000000..03d52d13 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.MySql.StoredProcedures.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..10667533 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.StoredProcedures")] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Production.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Staging.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.json b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.json new file mode 100644 index 00000000..d43a066a --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Api.Data.MySql.StoredProcedures/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/Dockerfile b/Api.Data.MySql.StoredProcedures/Dockerfile new file mode 100644 index 00000000..81df7ff6 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.MySql.StoredProcedures.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/LICENSE b/Api.Data.MySql.StoredProcedures/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.StoredProcedures/README.md b/Api.Data.MySql.StoredProcedures/README.md new file mode 100644 index 00000000..3fec788c --- /dev/null +++ b/Api.Data.MySql.StoredProcedures/README.md @@ -0,0 +1,38 @@ +# Api.Data.MySql.StoredProcedures + +> _Nano API application with mysql stored procedure data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been +simplified to showcase mysql stored procedures; full controllers are unnecessary. + +This example defines a stored procedure, and creates it during a migration. + +```csharp +migrationBuilder + .Sql(ExampleStoredProcedureDefinition.SQL); +``` + +The stored procedure is executed using Nano’s `IRepository.ExecuteAsync(...)` via this application's extension method `GetExampleResult`. The method simply wraps the +repository call to provide a clear, strongly typed invocation point for use by the controller. + +The following endpoint is available for testing: + +| Endpoint | Description | +| ------------------------------------------------------ | ------------------------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/stored-procedure` | Returns a simple `200 OK` response with the result of the stored procedure as response. | + +> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql/README.md#nanodatamysql)**. diff --git a/Api.Data.MySql.StoredProcedures/icon.png b/Api.Data.MySql.StoredProcedures/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Views/.docker/docker-compose.yml b/Api.Data.MySql.Views/.docker/docker-compose.yml new file mode 100644 index 00000000..8a5c7578 --- /dev/null +++ b/Api.Data.MySql.Views/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.mysql.views: + image: api.data.mysql.views + hostname: api-data-mysql-views + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.MySql.Views + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.MySql.Views/.dockerignore b/Api.Data.MySql.Views/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.MySql.Views/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.MySql.Views/.github/config/slack.yml b/Api.Data.MySql.Views/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.MySql.Views/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml b/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..2466d7bf --- /dev/null +++ b/Api.Data.MySql.Views/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.MySql.Views + IMAGE_NAME: api.data.mysql.views + SERVICE_NAME: api-data-mysql-views + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql.Views/.gitignore b/Api.Data.MySql.Views/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.MySql.Views/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql.Views/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql.Views/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.MySql.Views/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.MySql.Views/.kubernetes/autoscaler.yaml b/Api.Data.MySql.Views/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.MySql.Views/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql.Views/.kubernetes/configmap.yaml b/Api.Data.MySql.Views/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.MySql.Views/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql.Views/.kubernetes/deployment.yaml b/Api.Data.MySql.Views/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.MySql.Views/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.MySql.Views/.kubernetes/service.yaml b/Api.Data.MySql.Views/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.MySql.Views/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Properties/DoNotParallelize.cs b/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Tests.Api.Data.MySql.Views.csproj b/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Tests.Api.Data.MySql.Views.csproj new file mode 100644 index 00000000..8d17099b --- /dev/null +++ b/Api.Data.MySql.Views/.tests/Tests.Api.Data.MySql.Views/Tests.Api.Data.MySql.Views.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Api.Data.MySql.Views.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..32eca2ec --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.MySql.Views.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Example.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Example.cs new file mode 100644 index 00000000..e991ad4b --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Views.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/ExampleView.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/ExampleView.cs new file mode 100644 index 00000000..d0fcb181 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views.Models/ExampleView.cs @@ -0,0 +1,30 @@ +using System; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Views.Models; + +/// +/// Example View. +/// +public class ExampleView : BaseEntityView +{ + /// + /// Id. + /// + public virtual Guid Id { get; set; } + + /// + /// Created At. + /// + public virtual DateTimeOffset CreatedAt { get; set; } + + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; + + /// + /// Name Length. + /// + public virtual int NameLength { get; set; } +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views.sln b/Api.Data.MySql.Views/Api.Data.MySql.Views.sln new file mode 100644 index 00000000..eb10227b --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Views.Models", "Api.Data.MySql.Views.Models\Api.Data.MySql.Views.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Views", "Api.Data.MySql.Views\Api.Data.MySql.Views.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql.Views", ".tests\Tests.Api.Data.MySql.Views\Tests.Api.Data.MySql.Views.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Api.Data.MySql.Views.csproj b/Api.Data.MySql.Views/Api.Data.MySql.Views/Api.Data.MySql.Views.csproj new file mode 100644 index 00000000..c440c4e6 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Api.Data.MySql.Views.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExampleViewsController.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExampleViewsController.cs new file mode 100644 index 00000000..89264bf1 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExampleViewsController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Views.Models; +using Api.Data.MySql.Views.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Views.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleViewsController(ILogger logger, IRepository repository) + : BaseEntityViewController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExamplesController.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExamplesController.cs new file mode 100644 index 00000000..32f773ee --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Views.Models; +using Api.Data.MySql.Views.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Views.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..89e6a6e7 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,23 @@ +using System; +using Api.Data.MySql.Views.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Views.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleViewMapping.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleViewMapping.cs new file mode 100644 index 00000000..fade453c --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Mappings/ExampleViewMapping.cs @@ -0,0 +1,32 @@ +using System; +using Api.Data.MySql.Views.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Views.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleViewMapping : BaseEntityViewMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Id); + + builder + .Property(x => x.CreatedAt); + + builder + .Property(x => x.Name); + + builder + .Property(x => x.NameLength); + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContext.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContext.cs new file mode 100644 index 00000000..303dcd55 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.MySql.Views.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContextFactory.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..8c365ec7 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.MySql.Views.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Views/ExampleViewDefinition.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Views/ExampleViewDefinition.cs new file mode 100644 index 00000000..f0ac49d2 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Data/Views/ExampleViewDefinition.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Views.Models; + +namespace Api.Data.MySql.Views.Data.Views; + +internal static class ExampleViewDefinition +{ + internal const string SQL = $@" + CREATE OR REPLACE VIEW {nameof(ExampleView)} AS + SELECT + {nameof(Example.Id)}, + {nameof(Example.CreatedAt)}, + {nameof(Example.Name)}, + CHAR_LENGTH({nameof(Example.Name)}) AS {nameof(ExampleView.NameLength)} + FROM {nameof(Example)};"; +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local b/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local new file mode 100644 index 00000000..bac8ec3f --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.MySql.Views.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.Designer.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.Designer.cs new file mode 100644 index 00000000..f9ab6d19 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.Designer.cs @@ -0,0 +1,669 @@ +// +using System; +using Api.Data.MySql.Views.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Views.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415152009_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Views.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.MySql.Views.Models.ExampleView", b => + { + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NameLength") + .HasColumnType("int"); + + b.ToTable((string)null); + + b.ToView("ExampleView", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.cs new file mode 100644 index 00000000..ad56c19b --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152009_Initial.cs @@ -0,0 +1,596 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.MySql.Views.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.Designer.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.Designer.cs new file mode 100644 index 00000000..eda72e69 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.Designer.cs @@ -0,0 +1,669 @@ +// +using System; +using Api.Data.MySql.Views.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Views.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415152149_AddedView")] + partial class AddedView + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Views.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.MySql.Views.Models.ExampleView", b => + { + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NameLength") + .HasColumnType("int"); + + b.ToTable((string)null); + + b.ToView("ExampleView", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.cs new file mode 100644 index 00000000..70b7d706 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/20260415152149_AddedView.cs @@ -0,0 +1,24 @@ +using Api.Data.MySql.Views.Data.Views; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.MySql.Views.Migrations +{ + /// + public partial class AddedView : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder + .Sql(ExampleViewDefinition.SQL); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..9b0bbc85 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,666 @@ +// +using System; +using Api.Data.MySql.Views.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Views.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Views.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.MySql.Views.Models.ExampleView", b => + { + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Id") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("NameLength") + .HasColumnType("int"); + + b.ToTable((string)null); + + b.ToView("ExampleView", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Program.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Program.cs new file mode 100644 index 00000000..74ae1422 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.MySql.Views.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/Properties/InternalsVisibleTo.cs b/Api.Data.MySql.Views/Api.Data.MySql.Views/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..4a7c6b5e --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.MySql.Views")] \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Production.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Staging.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.json b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.json new file mode 100644 index 00000000..d43a066a --- /dev/null +++ b/Api.Data.MySql.Views/Api.Data.MySql.Views/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.MySql.Views/Dockerfile b/Api.Data.MySql.Views/Dockerfile new file mode 100644 index 00000000..9e00750f --- /dev/null +++ b/Api.Data.MySql.Views/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.MySql.Views.dll"] \ No newline at end of file diff --git a/Api.Data.MySql.Views/LICENSE b/Api.Data.MySql.Views/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.MySql.Views/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql.Views/README.md b/Api.Data.MySql.Views/README.md new file mode 100644 index 00000000..6706eeea --- /dev/null +++ b/Api.Data.MySql.Views/README.md @@ -0,0 +1,32 @@ +# Api.Data.MySql.Views + +> _Nano API application with mysql views data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**. Entity controllers have been +simplified to showcase mysql views; full controllers are unnecessary. + +An `ExampleView` entity model (deriving from `BaseEntityView`) has been added, along with a mapping class based on `BaseEntityViewMapping`. The corresponding database view has +been manually added in an empty migration. + +```csharp +migrationBuilder + .Sql(ExampleViewDefinition.SQL); +``` + +Also, an `ExampleViewsController` (deriving from `BaseEntityViewController`) has been added, exposing query actions for the view. + +> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql/README.md#nanodatamysql)**. diff --git a/Api.Data.MySql.Views/icon.png b/Api.Data.MySql.Views/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.MySql/.docker/docker-compose.yml b/Api.Data.MySql/.docker/docker-compose.yml new file mode 100644 index 00000000..e1757ea8 --- /dev/null +++ b/Api.Data.MySql/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.mysql: + image: api.data.mysql + hostname: api-data-mysql + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.MySql + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.MySql/.dockerignore b/Api.Data.MySql/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.MySql/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.MySql/.github/config/slack.yml b/Api.Data.MySql/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.MySql/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.MySql/.github/workflows/build-and-deploy.yml b/Api.Data.MySql/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..cba9bb65 --- /dev/null +++ b/Api.Data.MySql/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.MySql + IMAGE_NAME: api.data.mysql + SERVICE_NAME: api-data-mysql + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.MySql/.gitignore b/Api.Data.MySql/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.MySql/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.MySql/.kubernetes/auth-sql-secret.yaml b/Api.Data.MySql/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.MySql/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.MySql/.kubernetes/autoscaler.yaml b/Api.Data.MySql/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.MySql/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.MySql/.kubernetes/configmap.yaml b/Api.Data.MySql/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.MySql/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.MySql/.kubernetes/deployment.yaml b/Api.Data.MySql/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.MySql/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.MySql/.kubernetes/service.yaml b/Api.Data.MySql/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.MySql/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Properties/DoNotParallelize.cs b/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Tests.Api.Data.MySql.csproj b/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Tests.Api.Data.MySql.csproj new file mode 100644 index 00000000..af99d724 --- /dev/null +++ b/Api.Data.MySql/.tests/Tests.Api.Data.MySql/Tests.Api.Data.MySql.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj b/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj new file mode 100644 index 00000000..1a34a185 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql.Models/Api.Data.MySql.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + NU1902; NU1903; NU5104 + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.MySql/Api.Data.MySql.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..d239fafa --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.MySql.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/Example.cs b/Api.Data.MySql/Api.Data.MySql.Models/Example.cs new file mode 100644 index 00000000..581b42c9 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatable.cs b/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatable.cs new file mode 100644 index 00000000..cc3b1b10 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Models; + +/// +/// Example. +/// +public class ExampleCreatable : BaseEntityCreatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatableAndEditable.cs b/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatableAndEditable.cs new file mode 100644 index 00000000..ef663527 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql.Models/ExampleCreatableAndEditable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Models; + +/// +/// Example. +/// +public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/ExampleDeletable.cs b/Api.Data.MySql/Api.Data.MySql.Models/ExampleDeletable.cs new file mode 100644 index 00000000..2e82d8c0 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql.Models/ExampleDeletable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Models; + +/// +/// Example. +/// +public class ExampleDeletable : BaseEntityDeletable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.Models/ExampleUpdatable.cs b/Api.Data.MySql/Api.Data.MySql.Models/ExampleUpdatable.cs new file mode 100644 index 00000000..9be420fa --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql.Models/ExampleUpdatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.MySql.Models; + +/// +/// Example. +/// +public class ExampleUpdatable : BaseEntityUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql.sln b/Api.Data.MySql/Api.Data.MySql.sln new file mode 100644 index 00000000..7841d1e5 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql.Models", "Api.Data.MySql.Models\Api.Data.MySql.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.MySql", "Api.Data.MySql\Api.Data.MySql.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.MySql", ".tests\Tests.Api.Data.MySql\Tests.Api.Data.MySql.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.MySql/Api.Data.MySql/Api.Data.MySql.csproj b/Api.Data.MySql/Api.Data.MySql/Api.Data.MySql.csproj new file mode 100644 index 00000000..b6f023d3 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Api.Data.MySql.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreatableAndEditableController.cs new file mode 100644 index 00000000..1dc556cc --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreatableAndEditableController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Models; +using Api.Data.MySql.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) + : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreateablesController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreateablesController.cs new file mode 100644 index 00000000..279a0d52 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleCreateablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Models; +using Api.Data.MySql.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreateablesController(ILogger logger, IRepository repository) + : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleDeletablesController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleDeletablesController.cs new file mode 100644 index 00000000..8abbb5e2 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleDeletablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Models; +using Api.Data.MySql.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleDeletablesController(ILogger logger, IRepository repository) + : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleUpdatablesController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleUpdatablesController.cs new file mode 100644 index 00000000..26193a42 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Controllers/ExampleUpdatablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.MySql.Models; +using Api.Data.MySql.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleUpdatablesController(ILogger logger, IRepository repository) + : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Controllers/ExamplesController.cs b/Api.Data.MySql/Api.Data.MySql/Controllers/ExamplesController.cs new file mode 100644 index 00000000..89ce8a36 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Api.Data.MySql.Models; +using Api.Data.MySql.Models.Criterias; +using Nano.Data.Abstractions; + +namespace Api.Data.MySql.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs new file mode 100644 index 00000000..59b3f9fd --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.MySql.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Data.Mappings; + +/// +/// Example Creatable And Updatable Mapping. +/// +public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableMapping.cs new file mode 100644 index 00000000..563bf221 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleCreatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.MySql.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Data.Mappings; + +/// +/// Example Creatable Mapping. +/// +public class ExampleCreatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleDeletableMapping.cs new file mode 100644 index 00000000..08bbde35 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleDeletableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.MySql.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Data.Mappings; + +/// +/// Example Deletable Mapping. +/// +public class ExampleDeletableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..d33c25ae --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,27 @@ +using System; +using Api.Data.MySql.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name) + .IsUnique(); + } +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleUpdatableMapping.cs new file mode 100644 index 00000000..ecfb3efb --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Data/Mappings/ExampleUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.MySql.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.MySql.Data.Mappings; + +/// +/// Example Updatable Mapping. +/// +public class ExampleUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContext.cs b/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContext.cs new file mode 100644 index 00000000..aebe8955 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.MySql.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContextFactory.cs b/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..19978f90 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.MySql.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local b/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local new file mode 100644 index 00000000..2c0d1470 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.MySql.dll"] \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.Designer.cs b/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.Designer.cs new file mode 100644 index 00000000..088b3369 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.Designer.cs @@ -0,0 +1,781 @@ +// +using System; +using Api.Data.MySql.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260423071339_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("UX_Example_Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.MySql.Models.ExampleCreatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatable"); + }); + + modelBuilder.Entity("Api.Data.MySql.Models.ExampleCreatableAndEditable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatableAndEditable"); + }); + + modelBuilder.Entity("Api.Data.MySql.Models.ExampleDeletable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleDeletable"); + }); + + modelBuilder.Entity("Api.Data.MySql.Models.ExampleUpdatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleUpdatable"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.cs b/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.cs new file mode 100644 index 00000000..54072bfd --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Migrations/20260423071339_Initial.cs @@ -0,0 +1,742 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.MySql.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleCreatable", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleCreatable", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleCreatableAndEditable", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleCreatableAndEditable", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleDeletable", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleDeletable", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleUpdatable", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleUpdatable", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "UX_Example_Name", + table: "Example", + column: "Name", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_CreatedAt", + table: "ExampleCreatable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_IsDeleted", + table: "ExampleCreatable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_Name", + table: "ExampleCreatable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_CreatedAt", + table: "ExampleCreatableAndEditable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_IsDeleted", + table: "ExampleCreatableAndEditable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_Name", + table: "ExampleCreatableAndEditable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_CreatedAt", + table: "ExampleDeletable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_IsDeleted", + table: "ExampleDeletable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_Name", + table: "ExampleDeletable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_CreatedAt", + table: "ExampleUpdatable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_IsDeleted", + table: "ExampleUpdatable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_Name", + table: "ExampleUpdatable", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "ExampleCreatable"); + + migrationBuilder.DropTable( + name: "ExampleCreatableAndEditable"); + + migrationBuilder.DropTable( + name: "ExampleDeletable"); + + migrationBuilder.DropTable( + name: "ExampleUpdatable"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.MySql/Api.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.MySql/Api.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..968c51dc --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,778 @@ +// +using System; +using Api.Data.MySql.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.MySql.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.MySql.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name") + .IsUnique() + .HasDatabaseName("UX_Example_Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.MySql.Models.ExampleCreatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatable"); + }); + + modelBuilder.Entity("Api.Data.MySql.Models.ExampleCreatableAndEditable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatableAndEditable"); + }); + + modelBuilder.Entity("Api.Data.MySql.Models.ExampleDeletable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleDeletable"); + }); + + modelBuilder.Entity("Api.Data.MySql.Models.ExampleUpdatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleUpdatable"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.MySql/Api.Data.MySql/Program.cs b/Api.Data.MySql/Api.Data.MySql/Program.cs new file mode 100644 index 00000000..c8b8763b --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.MySql.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.MySql/Api.Data.MySql/Properties/InternalsVisibleTo.cs b/Api.Data.MySql/Api.Data.MySql/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..bfdea35b --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.MySql")] \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json b/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.Production.json b/Api.Data.MySql/Api.Data.MySql/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.Staging.json b/Api.Data.MySql/Api.Data.MySql/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.MySql/Api.Data.MySql/appsettings.json b/Api.Data.MySql/Api.Data.MySql/appsettings.json new file mode 100644 index 00000000..2871c661 --- /dev/null +++ b/Api.Data.MySql/Api.Data.MySql/appsettings.json @@ -0,0 +1,39 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + }, + "Documentation": { + "Name": "Application" + } + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } +} \ No newline at end of file diff --git a/Api.Data.MySql/Dockerfile b/Api.Data.MySql/Dockerfile new file mode 100644 index 00000000..d88ccf17 --- /dev/null +++ b/Api.Data.MySql/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.MySql.dll"] \ No newline at end of file diff --git a/Api.Data.MySql/LICENSE b/Api.Data.MySql/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.MySql/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.MySql/README.md b/Api.Data.MySql/README.md new file mode 100644 index 00000000..73a6b67a --- /dev/null +++ b/Api.Data.MySql/README.md @@ -0,0 +1,189 @@ +# Api.Data.MySql + +> _Nano API application with mysql data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from +the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings)**, +and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context)**. + +Additionally, the example shows how Nano **[Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories)** works along with the corresponding +entity controllers. For more information on controllers and how they are connected with entity models, see **[Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#controllers)**. + +A data health check is configured to target the database. +Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#health-checks)**. + +Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed +here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing +the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. + +> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql/README.md#nanodatamysql)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +Also, an initial migration has been added to the project. + +```powershell +dotnet ef migrations add Initial --project Api.Data.MySql +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "UseLazyLoading": false, + "StartupAction": "None", + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": false, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } +} +``` + +...and `appsettings.Development.json`. + +```json +"Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" +} +``` + +## Docker Compose +Added MySql as a service dependency in `docker-compose.yml`. + +```yaml +services: + api.data.mysql: + depends_on: + - database + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' +``` + +## Kubernetes +Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. + +```json +spec: + template: + spec: + containers: + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +``` + +Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. + +```yaml +- name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update; + apt-get install -y mysql-client; + + $userExists = mysql --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');" $env:SQL_MIGRATION_CONNECTIONSTRING; + + if ($userExists -eq 0) + { + mysql --connect-expired-password -e " ` + CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; ` + GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:SQL_USER'@'%'; ` + FLUSH PRIVILEGES;" $env:SQL_MIGRATION_CONNECTIONSTRING; + } +``` + +Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. diff --git a/Api.Data.MySql/icon.png b/Api.Data.MySql/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/.docker/docker-compose.yml b/Api.Data.PostgreSQL.Spatial/.docker/docker-compose.yml new file mode 100644 index 00000000..80392323 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.docker/docker-compose.yml @@ -0,0 +1,30 @@ +services: + api.data.postgresql.spatial: + image: api.data.postgresql.spatial + hostname: api-data-postgresql-spatial + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.PostgreSQL.Spatial + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: postgis/postgis:latest + ports: + - 5432:5432 + networks: + - network + environment: + POSTGRES_USER: sa + POSTGRES_PASSWORD: myPassword_123 + POSTGRES_DB: nanoDb + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.PostgreSQL.Spatial/.dockerignore b/Api.Data.PostgreSQL.Spatial/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.PostgreSQL.Spatial/.github/config/slack.yml b/Api.Data.PostgreSQL.Spatial/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..070e0c51 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.github/workflows/build-and-deploy.yml @@ -0,0 +1,243 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.PostgreSQL.Spatial + IMAGE_NAME: api.data.postgresql.spatial + SERVICE_NAME: api-data-postgresql-spatial + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-postgres-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "5432"; + $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y postgresql-client + + $userExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "CREATE ROLE $env:SQL_USER WITH LOGIN PASSWORD '$env:SQL_PASSWORD';" + } + + $userDbExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userDbExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT CONNECT ON DATABASE $env:SQL_NAME TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT USAGE ON SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:SQL_USER;" + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/.gitignore b/Api.Data.PostgreSQL.Spatial/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/auth-sql-secret.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/autoscaler.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.PostgreSQL.Spatial/.kubernetes/service.yaml b/Api.Data.PostgreSQL.Spatial/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Properties/DoNotParallelize.cs b/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Tests.Api.Data.PostgreSQL.Spatial.csproj b/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Tests.Api.Data.PostgreSQL.Spatial.csproj new file mode 100644 index 00000000..b54bb791 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/.tests/Tests.Api.Data.PostgreSQL.Spatial/Tests.Api.Data.PostgreSQL.Spatial.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj new file mode 100644 index 00000000..9b2d874d --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Api.Data.PostgreSQL.Spatial.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..9a8820f3 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; +using NetTopologySuite.Geometries; + +namespace Api.Data.PostgreSQL.Spatial.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Point. + /// + public virtual Point? Point { get; set; } + + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (this.Point != null) + { + expression + .IsWithinDistance(nameof(Example.Point), this.Point, 10000); + } + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Example.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Example.cs new file mode 100644 index 00000000..667f50ad --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.Models/Example.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; +using NetTopologySuite.Geometries; + +namespace Api.Data.PostgreSQL.Spatial.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Point. + /// + [Required] + public virtual Point Point { get; set; } = null!; + + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.sln b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.sln new file mode 100644 index 00000000..ca8b0c06 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.PostgreSQL.Spatial.Models", "Api.Data.PostgreSQL.Spatial.Models\Api.Data.PostgreSQL.Spatial.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.PostgreSQL.Spatial", "Api.Data.PostgreSQL.Spatial\Api.Data.PostgreSQL.Spatial.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.PostgreSQL.Spatial", ".tests\Tests.Api.Data.PostgreSQL.Spatial\Tests.Api.Data.PostgreSQL.Spatial.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.PostgreSQL", "..\..\Nano.Library\Nano.Data.PostgreSQL\Nano.Data.PostgreSQL.csproj", "{B898497D-7C77-3353-B323-399EBAF53DAC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B898497D-7C77-3353-B323-399EBAF53DAC} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.csproj b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.csproj new file mode 100644 index 00000000..3ee2a917 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Controllers/ExamplesController.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Controllers/ExamplesController.cs new file mode 100644 index 00000000..12dea508 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.PostgreSQL.Spatial.Models; +using Api.Data.PostgreSQL.Spatial.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.PostgreSQL.Spatial.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/Mappings/ExampleMapping.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..a252eeed --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,38 @@ +using System; +using Api.Data.PostgreSQL.Spatial.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using NetTopologySuite.Geometries; + +namespace Api.Data.PostgreSQL.Spatial.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Point) + .HasColumnType("geography(Point,4326)") + .HasDefaultValue(new Point(0, 0) { SRID = 4326 }) + .IsRequired(); + + builder + .HasIndex(x => x.Point) + .HasMethod("GIST"); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContext.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContext.cs new file mode 100644 index 00000000..a4a89e73 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.PostgreSQL.Spatial.Data; + +/// +public class PostgreSqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContextFactory.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContextFactory.cs new file mode 100644 index 00000000..b67d1e78 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Data/PostgreSqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.PostgreSQL; + +namespace Api.Data.PostgreSQL.Spatial.Data; + +/// +public class PostgreSqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local new file mode 100644 index 00000000..9e8b385b --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.Designer.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.Designer.cs new file mode 100644 index 00000000..860d3470 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.Designer.cs @@ -0,0 +1,661 @@ +// +using System; +using Api.Data.PostgreSQL.Spatial.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Api.Data.PostgreSQL.Spatial.Migrations +{ + [DbContext(typeof(PostgreSqlDbContext))] + [Migration("20260415150018_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.PostgreSQL.Spatial.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Point") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("geography(Point,4326)") + .HasDefaultValue((NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.HasIndex("Point"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Point"), "GIST"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityKey") + .HasColumnType("uuid"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("text"); + + b.Property("OldValue") + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("text"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.cs new file mode 100644 index 00000000..a11df532 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/20260415150018_Initial.cs @@ -0,0 +1,552 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Api.Data.PostgreSQL.Spatial.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:PostgresExtension:postgis", ",,"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedBy = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + EntityKey = table.Column(type: "uuid", nullable: false), + EntitySetName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EntityTypeName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + EntityState = table.Column(type: "integer", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + FriendlyName = table.Column(type: "text", nullable: true), + Xml = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IsActive = table.Column(type: "boolean", nullable: false, defaultValue: true), + UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "boolean", nullable: false), + PasswordHash = table.Column(type: "text", nullable: true), + SecurityStamp = table.Column(type: "text", nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true), + PhoneNumber = table.Column(type: "text", nullable: true), + PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), + TwoFactorEnabled = table.Column(type: "boolean", nullable: false), + LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), + LockoutEnabled = table.Column(type: "boolean", nullable: false), + AccessFailedCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Point = table.Column(type: "geography(Point,4326)", nullable: false, defaultValue: (NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")), + Name = table.Column(type: "text", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ParentId = table.Column(type: "uuid", nullable: false), + PropertyName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + RelationName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NewValue = table.Column(type: "text", nullable: true), + OldValue = table.Column(type: "text", nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + RoleId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Hash = table.Column(type: "text", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + RevokedAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + NewEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NewPhoneNumber = table.Column(type: "character varying(20)", maxLength: 20, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "text", nullable: false), + ProviderKey = table.Column(type: "text", nullable: false), + ProviderDisplayName = table.Column(type: "text", nullable: true), + UserId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + AppId = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Value = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + LoginProvider = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ApiKeyId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: false), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ApiKeyId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Point", + table: "Example", + column: "Point") + .Annotation("Npgsql:IndexMethod", "GIST"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/PostgreSqlDbContextModelSnapshot.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/PostgreSqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..9488cea7 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Migrations/PostgreSqlDbContextModelSnapshot.cs @@ -0,0 +1,658 @@ +// +using System; +using Api.Data.PostgreSQL.Spatial.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Api.Data.PostgreSQL.Spatial.Migrations +{ + [DbContext(typeof(PostgreSqlDbContext))] + partial class PostgreSqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.PostgreSQL.Spatial.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Point") + .IsRequired() + .ValueGeneratedOnAdd() + .HasColumnType("geography(Point,4326)") + .HasDefaultValue((NetTopologySuite.Geometries.Point)new NetTopologySuite.IO.WKTReader().Read("SRID=4326;POINT (0 0)")); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.HasIndex("Point"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Point"), "GIST"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityKey") + .HasColumnType("uuid"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("text"); + + b.Property("OldValue") + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("text"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs new file mode 100644 index 00000000..411bcfdc --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.PostgreSQL.Spatial.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.PostgreSQL; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Properties/InternalsVisibleTo.cs b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..e7caf604 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.PostgreSQL.Spatial")] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Development.json b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Development.json new file mode 100644 index 00000000..ddba6f26 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Production.json b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Staging.json b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.json b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.json new file mode 100644 index 00000000..d43a066a --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Api.Data.PostgreSQL.Spatial/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/Dockerfile b/Api.Data.PostgreSQL.Spatial/Dockerfile new file mode 100644 index 00000000..a1c9d769 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/LICENSE b/Api.Data.PostgreSQL.Spatial/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.PostgreSQL.Spatial/README.md b/Api.Data.PostgreSQL.Spatial/README.md new file mode 100644 index 00000000..457e15ca --- /dev/null +++ b/Api.Data.PostgreSQL.Spatial/README.md @@ -0,0 +1,25 @@ +# Api.Data.PostgreSQL.Spatial + +> _Nano API application with postgresql spatial data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.PostgreSQL)**. Entity controllers have been simplified to +showcase spatial types; full controllers are unnecessary. + +The `Example` entity now includes a `Point` property from `NetTopologySuite`. A query criterion has been added to check whether points are within a 10,000 meter distance. The +entity mappings for this spatial property have also been configured. Otherwise, no other changes were made. + +> 📖 Learn more about **[Nano.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.PostgreSQL/README.md#nanodatamysql)**. diff --git a/Api.Data.PostgreSQL.Spatial/icon.png b/Api.Data.PostgreSQL.Spatial/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.PostgreSQL/.docker/docker-compose.yml b/Api.Data.PostgreSQL/.docker/docker-compose.yml new file mode 100644 index 00000000..175643d3 --- /dev/null +++ b/Api.Data.PostgreSQL/.docker/docker-compose.yml @@ -0,0 +1,30 @@ +services: + api.data.postgresql: + image: api.data.postgresql + hostname: api-data-postgresql + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.PostgreSQL + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: postgis/postgis:latest + ports: + - 5432:5432 + networks: + - network + environment: + POSTGRES_USER: sa + POSTGRES_PASSWORD: myPassword_123 + POSTGRES_DB: nanoDb + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.PostgreSQL/.dockerignore b/Api.Data.PostgreSQL/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.PostgreSQL/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.PostgreSQL/.github/config/slack.yml b/Api.Data.PostgreSQL/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.PostgreSQL/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml b/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..2e82e0f1 --- /dev/null +++ b/Api.Data.PostgreSQL/.github/workflows/build-and-deploy.yml @@ -0,0 +1,243 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.PostgreSQL + IMAGE_NAME: api.data.postgresql + SERVICE_NAME: api-data-postgresql + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-postgres-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "5432"; + $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y postgresql-client + + $userExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "CREATE ROLE $env:SQL_USER WITH LOGIN PASSWORD '$env:SQL_PASSWORD';" + } + + $userDbExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userDbExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT CONNECT ON DATABASE $env:SQL_NAME TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT USAGE ON SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:SQL_USER;" + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_USER;Password=$env:SQL_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/.gitignore b/Api.Data.PostgreSQL/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.PostgreSQL/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml b/Api.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.PostgreSQL/.kubernetes/autoscaler.yaml b/Api.Data.PostgreSQL/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.PostgreSQL/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.PostgreSQL/.kubernetes/configmap.yaml b/Api.Data.PostgreSQL/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.PostgreSQL/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.PostgreSQL/.kubernetes/deployment.yaml b/Api.Data.PostgreSQL/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.PostgreSQL/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.PostgreSQL/.kubernetes/service.yaml b/Api.Data.PostgreSQL/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.PostgreSQL/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Properties/DoNotParallelize.cs b/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Tests.Api.Data.PostgreSQL.csproj b/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Tests.Api.Data.PostgreSQL.csproj new file mode 100644 index 00000000..ad37ca78 --- /dev/null +++ b/Api.Data.PostgreSQL/.tests/Tests.Api.Data.PostgreSQL/Tests.Api.Data.PostgreSQL.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj new file mode 100644 index 00000000..9b2d874d --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Api.Data.PostgreSQL.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..c7b5cc8a --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.PostgreSQL.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Example.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Example.cs new file mode 100644 index 00000000..e1e93b9f --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.PostgreSQL.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatable.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatable.cs new file mode 100644 index 00000000..d9fb5d23 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.PostgreSQL.Models; + +/// +/// Example. +/// +public class ExampleCreatable : BaseEntityCreatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatableAndEditable.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatableAndEditable.cs new file mode 100644 index 00000000..c2518608 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleCreatableAndEditable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.PostgreSQL.Models; + +/// +/// Example. +/// +public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleDeletable.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleDeletable.cs new file mode 100644 index 00000000..8f2eca9b --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleDeletable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.PostgreSQL.Models; + +/// +/// Example. +/// +public class ExampleDeletable : BaseEntityDeletable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleUpdatable.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleUpdatable.cs new file mode 100644 index 00000000..de5612f3 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.Models/ExampleUpdatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.PostgreSQL.Models; + +/// +/// Example. +/// +public class ExampleUpdatable : BaseEntityUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln new file mode 100644 index 00000000..6334c057 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.PostgreSQL.Models", "Api.Data.PostgreSQL.Models\Api.Data.PostgreSQL.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.PostgreSQL", "Api.Data.PostgreSQL\Api.Data.PostgreSQL.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.PostgreSQL", ".tests\Tests.Api.Data.PostgreSQL\Tests.Api.Data.PostgreSQL.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.PostgreSQL", "..\..\Nano.Library\Nano.Data.PostgreSQL\Nano.Data.PostgreSQL.csproj", "{B898497D-7C77-3353-B323-399EBAF53DAC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B898497D-7C77-3353-B323-399EBAF53DAC} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Api.Data.PostgreSQL.csproj b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Api.Data.PostgreSQL.csproj new file mode 100644 index 00000000..e3c713b8 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Api.Data.PostgreSQL.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreatableAndEditableController.cs new file mode 100644 index 00000000..99346c5d --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreatableAndEditableController.cs @@ -0,0 +1,15 @@ +using Api.Data.PostgreSQL.Models; +using Api.Data.PostgreSQL.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.PostgreSQL.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) + : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreateablesController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreateablesController.cs new file mode 100644 index 00000000..f344a80f --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleCreateablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.PostgreSQL.Models; +using Api.Data.PostgreSQL.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.PostgreSQL.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreateablesController(ILogger logger, IRepository repository) + : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleDeletablesController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleDeletablesController.cs new file mode 100644 index 00000000..79cf46be --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleDeletablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.PostgreSQL.Models; +using Api.Data.PostgreSQL.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.PostgreSQL.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleDeletablesController(ILogger logger, IRepository repository) + : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleUpdatablesController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleUpdatablesController.cs new file mode 100644 index 00000000..aedc1733 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExampleUpdatablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.PostgreSQL.Models; +using Api.Data.PostgreSQL.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.PostgreSQL.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleUpdatablesController(ILogger logger, IRepository repository) + : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExamplesController.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExamplesController.cs new file mode 100644 index 00000000..8045bbad --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.PostgreSQL.Models; +using Api.Data.PostgreSQL.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.PostgreSQL.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs new file mode 100644 index 00000000..466b2e12 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.PostgreSQL.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.PostgreSQL.Data.Mappings; + +/// +/// Example Creatable And Updatable Mapping. +/// +public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableMapping.cs new file mode 100644 index 00000000..68904cfd --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleCreatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.PostgreSQL.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.PostgreSQL.Data.Mappings; + +/// +/// Example Creatable Mapping. +/// +public class ExampleCreatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleDeletableMapping.cs new file mode 100644 index 00000000..a7b1ba84 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleDeletableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.PostgreSQL.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.PostgreSQL.Data.Mappings; + +/// +/// Example Deletable Mapping. +/// +public class ExampleDeletableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..b8e39f21 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.PostgreSQL.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.PostgreSQL.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleUpdatableMapping.cs new file mode 100644 index 00000000..b65030c2 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/Mappings/ExampleUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.PostgreSQL.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.PostgreSQL.Data.Mappings; + +/// +/// Example Updatable Mapping. +/// +public class ExampleUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContext.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContext.cs new file mode 100644 index 00000000..6f914d89 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.PostgreSQL.Data; + +/// +public class PostgreSqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs new file mode 100644 index 00000000..4ef0e410 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.PostgreSQL; + +namespace Api.Data.PostgreSQL.Data; + +/// +public class PostgreSqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local new file mode 100644 index 00000000..e3fe4a49 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.Designer.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.Designer.cs new file mode 100644 index 00000000..898f949a --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.Designer.cs @@ -0,0 +1,770 @@ +// +using System; +using Api.Data.PostgreSQL.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Api.Data.PostgreSQL.Migrations +{ + [DbContext(typeof(PostgreSqlDbContext))] + [Migration("20260415145934_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleCreatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatable"); + }); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleCreatableAndEditable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatableAndEditable"); + }); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleDeletable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleDeletable"); + }); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleUpdatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleUpdatable"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityKey") + .HasColumnType("uuid"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("text"); + + b.Property("OldValue") + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("text"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.cs new file mode 100644 index 00000000..85e4a0fb --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/20260415145934_Initial.cs @@ -0,0 +1,672 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Api.Data.PostgreSQL.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:PostgresExtension:postgis", ",,"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedBy = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + EntityKey = table.Column(type: "uuid", nullable: false), + EntitySetName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EntityTypeName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + EntityState = table.Column(type: "integer", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + FriendlyName = table.Column(type: "text", nullable: true), + Xml = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IsActive = table.Column(type: "boolean", nullable: false, defaultValue: true), + UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "boolean", nullable: false), + PasswordHash = table.Column(type: "text", nullable: true), + SecurityStamp = table.Column(type: "text", nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true), + PhoneNumber = table.Column(type: "text", nullable: true), + PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), + TwoFactorEnabled = table.Column(type: "boolean", nullable: false), + LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), + LockoutEnabled = table.Column(type: "boolean", nullable: false), + AccessFailedCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleCreatable", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleCreatable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleCreatableAndEditable", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleCreatableAndEditable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleDeletable", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleDeletable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleUpdatable", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleUpdatable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ParentId = table.Column(type: "uuid", nullable: false), + PropertyName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + RelationName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NewValue = table.Column(type: "text", nullable: true), + OldValue = table.Column(type: "text", nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + RoleId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Hash = table.Column(type: "text", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + RevokedAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + NewEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NewPhoneNumber = table.Column(type: "character varying(20)", maxLength: 20, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "text", nullable: false), + ProviderKey = table.Column(type: "text", nullable: false), + ProviderDisplayName = table.Column(type: "text", nullable: true), + UserId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + AppId = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Value = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + LoginProvider = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ApiKeyId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: false), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ApiKeyId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_CreatedAt", + table: "ExampleCreatable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_IsDeleted", + table: "ExampleCreatable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_Name", + table: "ExampleCreatable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_CreatedAt", + table: "ExampleCreatableAndEditable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_IsDeleted", + table: "ExampleCreatableAndEditable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_Name", + table: "ExampleCreatableAndEditable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_CreatedAt", + table: "ExampleDeletable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_IsDeleted", + table: "ExampleDeletable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_Name", + table: "ExampleDeletable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_CreatedAt", + table: "ExampleUpdatable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_IsDeleted", + table: "ExampleUpdatable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_Name", + table: "ExampleUpdatable", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "ExampleCreatable"); + + migrationBuilder.DropTable( + name: "ExampleCreatableAndEditable"); + + migrationBuilder.DropTable( + name: "ExampleDeletable"); + + migrationBuilder.DropTable( + name: "ExampleUpdatable"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..055a4aa4 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs @@ -0,0 +1,767 @@ +// +using System; +using Api.Data.PostgreSQL.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Api.Data.PostgreSQL.Migrations +{ + [DbContext(typeof(PostgreSqlDbContext))] + partial class PostgreSqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleCreatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatable"); + }); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleCreatableAndEditable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatableAndEditable"); + }); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleDeletable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleDeletable"); + }); + + modelBuilder.Entity("Api.Data.PostgreSQL.Models.ExampleUpdatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleUpdatable"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityKey") + .HasColumnType("uuid"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("text"); + + b.Property("OldValue") + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("text"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs new file mode 100644 index 00000000..7c7e2ead --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.PostgreSQL.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.PostgreSQL; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Properties/InternalsVisibleTo.cs b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..3690d0ad --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.PostgreSQL")] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Development.json b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Development.json new file mode 100644 index 00000000..ddba6f26 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Production.json b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Staging.json b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.json b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.json new file mode 100644 index 00000000..2871c661 --- /dev/null +++ b/Api.Data.PostgreSQL/Api.Data.PostgreSQL/appsettings.json @@ -0,0 +1,39 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + }, + "Documentation": { + "Name": "Application" + } + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } +} \ No newline at end of file diff --git a/Api.Data.PostgreSQL/Dockerfile b/Api.Data.PostgreSQL/Dockerfile new file mode 100644 index 00000000..754207ea --- /dev/null +++ b/Api.Data.PostgreSQL/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Api.Data.PostgreSQL/LICENSE b/Api.Data.PostgreSQL/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.PostgreSQL/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.PostgreSQL/README.md b/Api.Data.PostgreSQL/README.md new file mode 100644 index 00000000..982e730e --- /dev/null +++ b/Api.Data.PostgreSQL/README.md @@ -0,0 +1,204 @@ +# Api.Data.PostgreSQL + +> _Nano API application with postgre sql data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from +the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings)**, +and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context)**. + +Additionally, the example shows how Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories) works along with the corresponding +entity controllers. For more information on controllers and how they are connected with entity models, see **[Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#controllers)**. + +A data health check is configured to target the database. +Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#health-checks)**. + +Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed +here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing +the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. + +> 📖 Learn more about **[Nano.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.PostgreSQL/README.md#nanodatamysql)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +Also, an initial migration has been added to the project. + +```powershell +dotnet ef migrations add Initial --project Api.Data.PostgreSQL +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "UseLazyLoading": false, + "StartupAction": "None", + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": false, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } +} +``` + +...and `appsettings.Development.json` + +```json +"Data": { + "StartupAction": "Migrate", + "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" +} +``` + +## Docker Compose +Added PostgreSQL as a service dependency in `docker-compose.yml`. + +```yaml +services: + api.data.postgresql: + depends_on: + - database + + database: + image: postgis/postgis:latest + ports: + - 5432:5432 + networks: + - network + environment: + POSTGRES_USER: sa + POSTGRES_PASSWORD: myPassword_123 + POSTGRES_DB: nanoDb +``` + +## Kubernetes +Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. + +```json +spec: + template: + spec: + containers: + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SQL_NAME: nanoDb + SQL_USER: api-data-postgres-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +``` + +Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. + +```yaml +- name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "5432"; + $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y postgresql-client + + $userExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "CREATE ROLE $env:SQL_USER WITH LOGIN PASSWORD '$env:SQL_PASSWORD';" + } + + $userDbExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userDbExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT CONNECT ON DATABASE $env:SQL_NAME TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT USAGE ON SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:SQL_USER;" + } +``` + +Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. diff --git a/Api.Data.PostgreSQL/icon.png b/Api.Data.PostgreSQL/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/.docker/docker-compose.yml b/Api.Data.Repository.AutoSave/.docker/docker-compose.yml new file mode 100644 index 00000000..9b69f42b --- /dev/null +++ b/Api.Data.Repository.AutoSave/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.repository.autosave: + image: api.data.repository.autosave + hostname: api-data-repository-autosave + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.Repository.AutoSave + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.Repository.AutoSave/.dockerignore b/Api.Data.Repository.AutoSave/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.Repository.AutoSave/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.Repository.AutoSave/.github/config/slack.yml b/Api.Data.Repository.AutoSave/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.Repository.AutoSave/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml b/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..cac9c331 --- /dev/null +++ b/Api.Data.Repository.AutoSave/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.Repository.AutoSave + IMAGE_NAME: api.data.repository.autosave + SERVICE_NAME: api-data-repository-autosave + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/.gitignore b/Api.Data.Repository.AutoSave/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.Repository.AutoSave/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/.kubernetes/auth-sql-secret.yaml b/Api.Data.Repository.AutoSave/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.Repository.AutoSave/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.Repository.AutoSave/.kubernetes/autoscaler.yaml b/Api.Data.Repository.AutoSave/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.Repository.AutoSave/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml b/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.Repository.AutoSave/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml b/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.Repository.AutoSave/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.Repository.AutoSave/.kubernetes/service.yaml b/Api.Data.Repository.AutoSave/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.Repository.AutoSave/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Properties/DoNotParallelize.cs b/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Tests.Api.Data.Repository.AutoSave.csproj b/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Tests.Api.Data.Repository.AutoSave.csproj new file mode 100644 index 00000000..23e5ef1d --- /dev/null +++ b/Api.Data.Repository.AutoSave/.tests/Tests.Api.Data.Repository.AutoSave/Tests.Api.Data.Repository.AutoSave.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Api.Data.Repository.AutoSave.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Example.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Example.cs new file mode 100644 index 00000000..5890bfd9 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Repository.AutoSave.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.sln b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.sln new file mode 100644 index 00000000..13a77dea --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Repository.AutoSave.Models", "Api.Data.Repository.AutoSave.Models\Api.Data.Repository.AutoSave.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Repository.AutoSave", "Api.Data.Repository.AutoSave\Api.Data.Repository.AutoSave.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Repository.AutoSave", ".tests\Tests.Api.Data.Repository.AutoSave\Tests.Api.Data.Repository.AutoSave.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.csproj b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.csproj new file mode 100644 index 00000000..c2313257 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Controllers/ExamplesController.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Controllers/ExamplesController.cs new file mode 100644 index 00000000..c2750aa1 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Controllers/ExamplesController.cs @@ -0,0 +1,39 @@ +using Api.Data.Repository.AutoSave.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Api.Data.Repository.AutoSave.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository) +{ + /// + /// No Save Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("no-save")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task NoSaveAsync(CancellationToken cancellationToken = default) + { + await this.Repository + .AddAsync(new Example + { + Name = "name" + }, cancellationToken); + + return this.Ok("no-save"); + } +} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/Mappings/ExampleMapping.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..cd6d7970 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.Repository.AutoSave.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.Repository.AutoSave.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContext.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContext.cs new file mode 100644 index 00000000..459fd7ec --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.Repository.AutoSave.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContextFactory.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..8c372d43 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.Repository.AutoSave.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local new file mode 100644 index 00000000..265ee7c3 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.Repository.AutoSave.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.Designer.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.Designer.cs new file mode 100644 index 00000000..a9a96364 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.Designer.cs @@ -0,0 +1,651 @@ +// +using System; +using Api.Data.Repository.AutoSave.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Repository.AutoSave.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415150030_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Repository.AutoSave.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.cs new file mode 100644 index 00000000..35d6f304 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/20260415150030_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Repository.AutoSave.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..8b37879c --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,648 @@ +// +using System; +using Api.Data.Repository.AutoSave.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Repository.AutoSave.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Repository.AutoSave.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Program.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Program.cs new file mode 100644 index 00000000..a94ed0a2 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.Repository.AutoSave.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Properties/InternalsVisibleTo.cs b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..1ba7f8ee --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.Repository.AutoSave")] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Production.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Staging.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.json b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.json new file mode 100644 index 00000000..91802788 --- /dev/null +++ b/Api.Data.Repository.AutoSave/Api.Data.Repository.AutoSave/appsettings.json @@ -0,0 +1,19 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null, + "Repository": { + "UseAutoSave": false + } + } +} \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/Dockerfile b/Api.Data.Repository.AutoSave/Dockerfile new file mode 100644 index 00000000..3c02c42d --- /dev/null +++ b/Api.Data.Repository.AutoSave/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.Repository.AutoSave.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/LICENSE b/Api.Data.Repository.AutoSave/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.Repository.AutoSave/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Repository.AutoSave/README.md b/Api.Data.Repository.AutoSave/README.md new file mode 100644 index 00000000..cdd418dd --- /dev/null +++ b/Api.Data.Repository.AutoSave/README.md @@ -0,0 +1,44 @@ +# Api.Data.Repository.AutoSave + +> _Nano API application with data repository autosave disabled._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to +demonstrate repository autosave. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. + +In this application, autosave has been disabled. When the endpoint is invoked, Nano attempts to persist the entity to the database. However, the `IRepository` does +not commit the changes because autosave is disabled and `SaveChangesAsync(...)` is not invoked manually within the controller action. As a result, the changes are not +written to the database. If you switch the configuration to enable autosave, you will see that Nano then saves the changes. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ---------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/no-save` | Returns a simple `200 OK` response, while trying to add a new `Example`, changes are never saved. | + +> 📖 Learn more about **[Nano Data Repository Autosave](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#autosave)**. + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "Repository": { + "UseAutoSave": false + } +} +``` diff --git a/Api.Data.Repository.AutoSave/icon.png b/Api.Data.Repository.AutoSave/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.Repository.Includes/.docker/docker-compose.yml b/Api.Data.Repository.Includes/.docker/docker-compose.yml new file mode 100644 index 00000000..98336e73 --- /dev/null +++ b/Api.Data.Repository.Includes/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.repository.includes: + image: api.data.repository.includes + hostname: api-data-repository-includes + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.Repository.Includes + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.Repository.Includes/.dockerignore b/Api.Data.Repository.Includes/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.Repository.Includes/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.Repository.Includes/.github/config/slack.yml b/Api.Data.Repository.Includes/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.Repository.Includes/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml b/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..8638dea7 --- /dev/null +++ b/Api.Data.Repository.Includes/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.Repository.Includes + IMAGE_NAME: api.data.repository.includes + SERVICE_NAME: api-data-repository-includes + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/.gitignore b/Api.Data.Repository.Includes/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.Repository.Includes/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Repository.Includes/.kubernetes/auth-sql-secret.yaml b/Api.Data.Repository.Includes/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.Repository.Includes/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.Repository.Includes/.kubernetes/autoscaler.yaml b/Api.Data.Repository.Includes/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.Repository.Includes/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Repository.Includes/.kubernetes/configmap.yaml b/Api.Data.Repository.Includes/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.Repository.Includes/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Repository.Includes/.kubernetes/deployment.yaml b/Api.Data.Repository.Includes/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.Repository.Includes/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.Repository.Includes/.kubernetes/service.yaml b/Api.Data.Repository.Includes/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.Repository.Includes/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Properties/DoNotParallelize.cs b/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Tests.Api.Data.Repository.Includes.csproj b/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Tests.Api.Data.Repository.Includes.csproj new file mode 100644 index 00000000..423e9dd5 --- /dev/null +++ b/Api.Data.Repository.Includes/.tests/Tests.Api.Data.Repository.Includes/Tests.Api.Data.Repository.Includes.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Api.Data.Repository.Includes.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Customer.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Customer.cs new file mode 100644 index 00000000..13472729 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Customer.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Annotations; +using Nano.Data.Abstractions.Config.Enums; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Repository.Includes.Models; + +/// +/// Customer. +/// +public class Customer : BaseEntity +{ + /// + /// Profile Id. + /// + [Required] + public virtual Guid ProfileId { get; set; } + + /// + /// Profile. + /// + public virtual CustomerProfile Profile { get; set; } = null!; + + /// + /// Order. + /// + [Required] + [Include(QuerySplitBehavior.SplitQuery)] + public virtual ICollection Orders { get; set; } = []; +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/CustomerProfile.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/CustomerProfile.cs new file mode 100644 index 00000000..5f0746e6 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/CustomerProfile.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Repository.Includes.Models; + +/// +/// Customer Profile. +/// +public class CustomerProfile : BaseEntity +{ + /// + /// Customer. + /// + public virtual Customer Customer { get; set; } = null!; + + /// + /// Name. + /// + [Required] + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Order.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Order.cs new file mode 100644 index 00000000..2b813b8f --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Order.cs @@ -0,0 +1,34 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Annotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Repository.Includes.Models; + +/// +/// Order. +/// +public class Order : BaseEntity +{ + /// + /// Customer Id. + /// + [Required] + public virtual Guid CustomerId { get; set; } + + /// + /// Customer. + /// + public virtual Customer Customer { get; set; } = null!; + + /// + /// Payment Id. + /// + public virtual Guid? PaymentId { get; set; } + + /// + /// Payment. + /// + [Include] + public virtual Payment? Payment { get; set; } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Payment.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Payment.cs new file mode 100644 index 00000000..d45920c6 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.Models/Payment.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Repository.Includes.Models; + +/// +/// Payment. +/// +public class Payment : BaseEntity +{ + /// + /// Order. + /// + public virtual Order Order { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes.sln b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.sln new file mode 100644 index 00000000..dc095e67 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Repository.Includes.Models", "Api.Data.Repository.Includes.Models\Api.Data.Repository.Includes.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Repository.Includes", "Api.Data.Repository.Includes\Api.Data.Repository.Includes.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Repository.Includes", ".tests\Tests.Api.Data.Repository.Includes\Tests.Api.Data.Repository.Includes.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Api.Data.Repository.Includes.csproj b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Api.Data.Repository.Includes.csproj new file mode 100644 index 00000000..70ea6563 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Api.Data.Repository.Includes.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/ExamplesController.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/ExamplesController.cs new file mode 100644 index 00000000..2ae0ebfd --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/ExamplesController.cs @@ -0,0 +1,146 @@ +using Api.Data.Repository.Includes.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.Data.Repository.Includes.Controllers.Response; + +namespace Api.Data.Repository.Includes.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository) +{ + /// + /// Create Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpPost] + [Route("create")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task CreateAsync(CancellationToken cancellationToken = default) + { + await this.CreateCustomerAsync(cancellationToken); + } + + /// + /// Include Action. + /// + /// The include-depth. + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("include")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task IncludeAsync([FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + var customer = await this.Repository + .GetFirstAsync(x => true, includeDepth, cancellationToken); + + return this.Ok(customer); + } + + /// + /// Create And Include Action. + /// + /// The include-depth. + /// The cancellation token. + /// A Cusetomer. + /// Success. + [HttpGet] + [Route("create-and-include")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task CreateAndIncludeAsync([FromQuery]int? includeDepth, CancellationToken cancellationToken = default) + { + var customer = await this.CreateCustomerAsync(cancellationToken); + + customer = await this.Repository + .GetAsync(customer.Id, includeDepth, cancellationToken); + + return this.Ok(customer); + } + + /// + /// Not Include Action. + /// + /// The include-depth. + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("not-include")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task NotIncludeAsync([FromQuery] int? includeDepth, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + var customer = new CustomerResponse + { + Id = Guid.NewGuid(), + Profile = new CustomerProfileResponse + { + Id = Guid.NewGuid(), + Name = "name" + }, + Orders = new List + { + new() + { + Id = Guid.NewGuid(), + Payment = new PaymentResponse + { + Id = Guid.NewGuid() + } + }, + new() + { + Id = Guid.NewGuid(), + Payment = new PaymentResponse + { + Id = Guid.NewGuid() + } + } + } + }; + + return this.Ok(customer); + } + + + private async Task CreateCustomerAsync(CancellationToken cancellationToken = default) + { + var customer = await this.Repository + .AddAsync(new Customer + { + Profile = new CustomerProfile + { + Name = "name" + }, + Orders = new List + { + new() + { + Payment = new Payment() + }, + new() + { + Payment = new Payment() + } + } + }, cancellationToken); + + return customer; + } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerProfileResponse.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerProfileResponse.cs new file mode 100644 index 00000000..2a88f64c --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerProfileResponse.cs @@ -0,0 +1,19 @@ +using System; + +namespace Api.Data.Repository.Includes.Controllers.Response; + +/// +/// Customer Profile Response. +/// +public class CustomerProfileResponse +{ + /// + /// Id. + /// + public virtual Guid Id { get; set; } + + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerResponse.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerResponse.cs new file mode 100644 index 00000000..5e64558c --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/CustomerResponse.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace Api.Data.Repository.Includes.Controllers.Response; + +/// +/// Customer Response. +/// +public class CustomerResponse +{ + /// + /// Id. + /// + public virtual Guid Id { get; set; } + + /// + /// Profile. + /// + public virtual CustomerProfileResponse Profile { get; set; } = null!; + + /// + /// Order. + /// + public virtual ICollection Orders { get; set; } = []; +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/OrderResponse.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/OrderResponse.cs new file mode 100644 index 00000000..7bf83f1e --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/OrderResponse.cs @@ -0,0 +1,24 @@ +using System; + +namespace Api.Data.Repository.Includes.Controllers.Response; + +/// +/// Order Response. +/// +public class OrderResponse +{ + /// + /// Id. + /// + public virtual Guid Id { get; set; } + + /// + /// Customer. + /// + public virtual CustomerResponse Customer { get; set; } = null!; + + /// + /// Payment. + /// + public virtual PaymentResponse? Payment { get; set; } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/PaymentResponse.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/PaymentResponse.cs new file mode 100644 index 00000000..22afcbb5 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Controllers/Response/PaymentResponse.cs @@ -0,0 +1,14 @@ +using System; + +namespace Api.Data.Repository.Includes.Controllers.Response; + +/// +/// Payment Response. +/// +public class PaymentResponse +{ + /// + /// Id. + /// + public virtual Guid Id { get; set; } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerMapping.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerMapping.cs new file mode 100644 index 00000000..16e0a485 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerMapping.cs @@ -0,0 +1,30 @@ +using System; +using Api.Data.Repository.Includes.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.Repository.Includes.Data.Mappings; + +/// +/// Customer Mapping. +/// +public class CustomerMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .HasOne(x => x.Profile) + .WithOne(x => x.Customer) + .IsRequired(); + + builder + .HasMany(x => x.Orders) + .WithOne(x => x.Customer) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerProfileMapping.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerProfileMapping.cs new file mode 100644 index 00000000..d61d7b62 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/CustomerProfileMapping.cs @@ -0,0 +1,28 @@ +using System; +using Api.Data.Repository.Includes.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.Repository.Includes.Data.Mappings; + +/// +/// Customer Profile Mapping. +/// +public class CustomerProfileMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .HasOne(x => x.Customer) + .WithOne(x => x.Profile); + + builder + .Property(x => x.Name) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/OrderMapping.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/OrderMapping.cs new file mode 100644 index 00000000..d2bc2cc8 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/OrderMapping.cs @@ -0,0 +1,29 @@ +using System; +using Api.Data.Repository.Includes.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.Repository.Includes.Data.Mappings; + +/// +/// Example Mapping. +/// +public class OrderMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .HasOne(x => x.Customer) + .WithMany(x => x.Orders) + .IsRequired(); + + builder + .HasOne(x => x.Payment) + .WithOne(x => x.Order); + } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/PaymentMapping.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/PaymentMapping.cs new file mode 100644 index 00000000..b2d147ed --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/Mappings/PaymentMapping.cs @@ -0,0 +1,24 @@ +using System; +using Api.Data.Repository.Includes.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.Repository.Includes.Data.Mappings; + +/// +/// Example Mapping. +/// +public class PaymentMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .HasOne(x => x.Order) + .WithOne(x => x.Payment); + } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContext.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContext.cs new file mode 100644 index 00000000..b34dbcf7 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.Repository.Includes.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContextFactory.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..92a54df6 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.Repository.Includes.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local new file mode 100644 index 00000000..f67686a8 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.Repository.Includes.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.Designer.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.Designer.cs new file mode 100644 index 00000000..37f28f9a --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.Designer.cs @@ -0,0 +1,791 @@ +// +using System; +using Api.Data.Repository.Includes.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Repository.Includes.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415150006_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("ProfileId") + .IsUnique() + .HasDatabaseName("UX_Customer_ProfileId"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.CustomerProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("CustomerProfile"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CustomerId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("PaymentId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("CustomerId"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("PaymentId") + .IsUnique() + .HasDatabaseName("UX_Order_PaymentId"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Payment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Payment"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => + { + b.HasOne("Api.Data.Repository.Includes.Models.CustomerProfile", "Profile") + .WithOne("Customer") + .HasForeignKey("Api.Data.Repository.Includes.Models.Customer", "ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Order", b => + { + b.HasOne("Api.Data.Repository.Includes.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Api.Data.Repository.Includes.Models.Payment", "Payment") + .WithOne("Order") + .HasForeignKey("Api.Data.Repository.Includes.Models.Order", "PaymentId"); + + b.Navigation("Customer"); + + b.Navigation("Payment"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.CustomerProfile", b => + { + b.Navigation("Customer") + .IsRequired(); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Payment", b => + { + b.Navigation("Order") + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.cs new file mode 100644 index 00000000..a0cad7c7 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/20260415150006_Initial.cs @@ -0,0 +1,717 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Repository.Includes.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "CustomerProfile", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_CustomerProfile", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Payment", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Payment", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Customer", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ProfileId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Customer", x => x.Id); + table.ForeignKey( + name: "FK_Customer_CustomerProfile_ProfileId", + column: x => x.ProfileId, + principalTable: "CustomerProfile", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Order", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CustomerId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PaymentId = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Order", x => x.Id); + table.ForeignKey( + name: "FK_Order_Customer_CustomerId", + column: x => x.CustomerId, + principalTable: "Customer", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Order_Payment_PaymentId", + column: x => x.PaymentId, + principalTable: "Payment", + principalColumn: "Id"); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Customer_CreatedAt", + table: "Customer", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Customer_IsDeleted", + table: "Customer", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "UX_Customer_ProfileId", + table: "Customer", + column: "ProfileId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_CustomerProfile_CreatedAt", + table: "CustomerProfile", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_CustomerProfile_IsDeleted", + table: "CustomerProfile", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Order_CreatedAt", + table: "Order", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Order_CustomerId", + table: "Order", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_IsDeleted", + table: "Order", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "UX_Order_PaymentId", + table: "Order", + column: "PaymentId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Payment_CreatedAt", + table: "Payment", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Payment_IsDeleted", + table: "Payment", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Order"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "Customer"); + + migrationBuilder.DropTable( + name: "Payment"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + + migrationBuilder.DropTable( + name: "CustomerProfile"); + } + } +} diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..7028a6e8 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,788 @@ +// +using System; +using Api.Data.Repository.Includes.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Repository.Includes.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("ProfileId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("ProfileId") + .IsUnique() + .HasDatabaseName("UX_Customer_ProfileId"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.CustomerProfile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("CustomerProfile"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CustomerId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("PaymentId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("CustomerId"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("PaymentId") + .IsUnique() + .HasDatabaseName("UX_Order_PaymentId"); + + b.ToTable("Order"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Payment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("Payment"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => + { + b.HasOne("Api.Data.Repository.Includes.Models.CustomerProfile", "Profile") + .WithOne("Customer") + .HasForeignKey("Api.Data.Repository.Includes.Models.Customer", "ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Order", b => + { + b.HasOne("Api.Data.Repository.Includes.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Api.Data.Repository.Includes.Models.Payment", "Payment") + .WithOne("Order") + .HasForeignKey("Api.Data.Repository.Includes.Models.Order", "PaymentId"); + + b.Navigation("Customer"); + + b.Navigation("Payment"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.CustomerProfile", b => + { + b.Navigation("Customer") + .IsRequired(); + }); + + modelBuilder.Entity("Api.Data.Repository.Includes.Models.Payment", b => + { + b.Navigation("Order") + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Program.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Program.cs new file mode 100644 index 00000000..133b1047 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.Repository.Includes.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Properties/InternalsVisibleTo.cs b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..93b5d5b4 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.Repository.Includes")] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Production.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Staging.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.json b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.json new file mode 100644 index 00000000..d52d2638 --- /dev/null +++ b/Api.Data.Repository.Includes/Api.Data.Repository.Includes/appsettings.json @@ -0,0 +1,19 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null, + "Repository": { + "QueryIncludeDepth": 3 + } + } +} \ No newline at end of file diff --git a/Api.Data.Repository.Includes/Dockerfile b/Api.Data.Repository.Includes/Dockerfile new file mode 100644 index 00000000..51c6edab --- /dev/null +++ b/Api.Data.Repository.Includes/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.Repository.Includes.dll"] \ No newline at end of file diff --git a/Api.Data.Repository.Includes/LICENSE b/Api.Data.Repository.Includes/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.Repository.Includes/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Repository.Includes/README.md b/Api.Data.Repository.Includes/README.md new file mode 100644 index 00000000..271b41f4 --- /dev/null +++ b/Api.Data.Repository.Includes/README.md @@ -0,0 +1,55 @@ +# Api.Data.Repository.Includes + +> _Nano API application with data repository include annotations._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to +demonstrate repository include annotations. Entity controllers have been simplified to showcase include annotation; full controllers are unnecessary. + +The endpoints can be invoked with the `includeDepth` query parameter to override the default configured depth. This makes it easy to experiment with different values +and observe how the returned object graph changes as Nano resolves deeper levels of includes. + +All the navigations in the object graph is annotated with `IncludeAttribute`, except for `Customer.Profile`. Because of this, it is not exposed during +response serialization, even that the instance is already loaded in the data context. Only properties explicitly marked for inclusion are serialized in the response. You +can read more about this behavior in the [Response Serialization](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#response-serialization) section. + +Observe how includes and nested includes appear in the response after the entities have been created and subsequently retrieved +using `IRepository.GetAsync(...)`. This demonstrates how Nano automatically resolves and serializes the configured include graph according to the effective depth. + +Another endpoint returns plain objects that do not implement `IEntity`, so the full object graph is always serialized. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/create` | Returns a simple `200 OK` response. Creates a Customer entity with related navigations. | +| `http://localhost:8080/api/examples/include` | Returns a simple `200 OK` response with `Customer` entity and nested included navigation properties. Requires a `Cusetomer` entity to be created first. | +| `http://localhost:8080/api/examples/create-and-include` | Returns a simple `200 OK` response. Creates a `Customer` entity and nested included navigation properties, and returns it. ⚠️ If request `includeDepth` is lower than configuration, serialization still exposes the depth using the confoguration. | +| `http://localhost:8080/api/examples/not-include` | Returns a simple `200 OK` response with `CustomerResponse`, that is not `IEntity`, and all properties are serialzied and exposed. | + +> 📖 Learn more about **[Nano Include Annotation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#include-annotation)**. + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "Repository": { + "QueryIncludeDepth": 3 + } +} +``` diff --git a/Api.Data.Repository.Includes/icon.png b/Api.Data.Repository.Includes/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.SoftDelete/.docker/docker-compose.yml b/Api.Data.SoftDelete/.docker/docker-compose.yml new file mode 100644 index 00000000..2182ed3f --- /dev/null +++ b/Api.Data.SoftDelete/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.repository.softdelete: + image: api.data.repository.softdelete + hostname: api-data-softdelete + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.SoftDelete + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.SoftDelete/.dockerignore b/Api.Data.SoftDelete/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.SoftDelete/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.SoftDelete/.github/config/slack.yml b/Api.Data.SoftDelete/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.SoftDelete/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml b/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..957cec57 --- /dev/null +++ b/Api.Data.SoftDelete/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.SoftDelete + IMAGE_NAME: api.data.softdelete + SERVICE_NAME: api-data-softdelete + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.SoftDelete/.gitignore b/Api.Data.SoftDelete/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.SoftDelete/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.SoftDelete/.kubernetes/auth-sql-secret.yaml b/Api.Data.SoftDelete/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.SoftDelete/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.SoftDelete/.kubernetes/autoscaler.yaml b/Api.Data.SoftDelete/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.SoftDelete/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.SoftDelete/.kubernetes/configmap.yaml b/Api.Data.SoftDelete/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.SoftDelete/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.SoftDelete/.kubernetes/deployment.yaml b/Api.Data.SoftDelete/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.SoftDelete/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.SoftDelete/.kubernetes/service.yaml b/Api.Data.SoftDelete/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.SoftDelete/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Properties/DoNotParallelize.cs b/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Tests.Api.Data.SoftDelete.csproj b/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Tests.Api.Data.SoftDelete.csproj new file mode 100644 index 00000000..33c551d0 --- /dev/null +++ b/Api.Data.SoftDelete/.tests/Tests.Api.Data.SoftDelete/Tests.Api.Data.SoftDelete.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Api.Data.SoftDelete.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..b5bce1e9 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.SoftDelete.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Example.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Example.cs new file mode 100644 index 00000000..d9c21ef1 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete.Models/Example.cs @@ -0,0 +1,15 @@ +using Nano.Data.Abstractions.Models; +using Nano.Data.Abstractions.Models.Abstractions; + +namespace Api.Data.SoftDelete.Models; + +/// +/// Example. +/// +public class Example : BaseEntity, IEntitySoftDeletable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete.sln b/Api.Data.SoftDelete/Api.Data.SoftDelete.sln new file mode 100644 index 00000000..6bc947ce --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SoftDelete.Models", "Api.Data.SoftDelete.Models\Api.Data.SoftDelete.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SoftDelete", "Api.Data.SoftDelete\Api.Data.SoftDelete.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.SoftDelete", ".tests\Tests.Api.Data.SoftDelete\Tests.Api.Data.SoftDelete.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Api.Data.SoftDelete.csproj b/Api.Data.SoftDelete/Api.Data.SoftDelete/Api.Data.SoftDelete.csproj new file mode 100644 index 00000000..03912446 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Api.Data.SoftDelete.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Controllers/ExamplesController.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Controllers/ExamplesController.cs new file mode 100644 index 00000000..545fb607 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SoftDelete.Models; +using Api.Data.SoftDelete.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SoftDelete.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/Mappings/ExampleMapping.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..f16a7a45 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,39 @@ +using System; +using Api.Data.SoftDelete.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using Nano.Data.Mappings.Extensions; + +namespace Api.Data.SoftDelete.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + + builder + .OnDeleting(x => + { + Console.WriteLine("OnDeleting"); + }); + + builder + .OnDeleted(x => + { + Console.WriteLine("OnDeleted"); + }); + } +} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContext.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContext.cs new file mode 100644 index 00000000..e046a986 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.SoftDelete.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContextFactory.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..2ab2f480 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.SoftDelete.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local b/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local new file mode 100644 index 00000000..f0398733 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.SoftDelete.dll"] \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.Designer.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.Designer.cs new file mode 100644 index 00000000..ac78cc0c --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.Designer.cs @@ -0,0 +1,651 @@ +// +using System; +using Api.Data.SoftDelete.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.SoftDelete.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415151003_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.SoftDelete.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.cs new file mode 100644 index 00000000..a2ec8ea4 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/20260415151003_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.SoftDelete.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..4f38b424 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,648 @@ +// +using System; +using Api.Data.SoftDelete.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.SoftDelete.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.SoftDelete.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Program.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Program.cs new file mode 100644 index 00000000..25e7035f --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.SoftDelete.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/Properties/InternalsVisibleTo.cs b/Api.Data.SoftDelete/Api.Data.SoftDelete/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..9a763dd3 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.SoftDelete")] \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Production.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Staging.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.json b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.json new file mode 100644 index 00000000..d43a066a --- /dev/null +++ b/Api.Data.SoftDelete/Api.Data.SoftDelete/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.SoftDelete/Dockerfile b/Api.Data.SoftDelete/Dockerfile new file mode 100644 index 00000000..4c606a9e --- /dev/null +++ b/Api.Data.SoftDelete/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.SoftDelete.dll"] \ No newline at end of file diff --git a/Api.Data.SoftDelete/LICENSE b/Api.Data.SoftDelete/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.SoftDelete/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.SoftDelete/README.md b/Api.Data.SoftDelete/README.md new file mode 100644 index 00000000..4203d52b --- /dev/null +++ b/Api.Data.SoftDelete/README.md @@ -0,0 +1,29 @@ +# Api.Data.SoftDelete + +> _Nano API application with data soft delete._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to +demonstrate soft delete. Entity controllers have been simplified to showcase autosave; full controllers are unnecessary. + +The `Example` entity implements `IEntitySoftDeletable`, so when an entity is deleted, it is not removed from the database but is marked as deleted +by setting the `IsDeleted` property. + +The data mapping also includes two triggers for `OnDeleting` and `OnDeleted` to show that they are invoked also when soft-deleting entity models. + +Open the database and notice that the created `Example` entity has a non-zero `IsDeleted` value, indicating it has been soft-deleted. + +> 📖 Learn more about **[Nano Data Soft Delete](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#soft-delete)**. diff --git a/Api.Data.SoftDelete/icon.png b/Api.Data.SoftDelete/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.SqLite/.docker/docker-compose.yml b/Api.Data.SqLite/.docker/docker-compose.yml new file mode 100644 index 00000000..28072671 --- /dev/null +++ b/Api.Data.SqLite/.docker/docker-compose.yml @@ -0,0 +1,18 @@ +services: + api.data.sqlite: + image: api.data.sqlite + hostname: api-data-sqlite + build: + context: ../Api.Data.SqLite + dockerfile: Dockerfile.Local + restart: on-failure + ports: + - 8080:8080 + volumes: + - ./bin/data:/mnt/data + networks: + - network + +networks: + network: + driver: bridge \ No newline at end of file diff --git a/Api.Data.SqLite/.dockerignore b/Api.Data.SqLite/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.SqLite/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.SqLite/.github/config/slack.yml b/Api.Data.SqLite/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.SqLite/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.SqLite/.github/workflows/build-and-deploy.yml b/Api.Data.SqLite/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..98ad1455 --- /dev/null +++ b/Api.Data.SqLite/.github/workflows/build-and-deploy.yml @@ -0,0 +1,209 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.SqLite + IMAGE_NAME: api.data.sqlite + SERVICE_NAME: api-data-sqlite + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_SIZE: 10Gi + SQL_MIGRATION_CONNECTIONSTRING: "Data Source=/mnt/data/{{ env.SQL_NAME }}.sqlite" +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration + shell: pwsh + run: | + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/data-storageclass.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/data-storageclass.tmp.yaml; + kubectl apply -f .kubernetes/data-storageclass.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/data-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/data-pvc.tmp.yaml; + kubectl apply -f .kubernetes/data-pvc.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.SqLite/.gitignore b/Api.Data.SqLite/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.SqLite/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.SqLite/.kubernetes/autoscaler.yaml b/Api.Data.SqLite/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.SqLite/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.SqLite/.kubernetes/configmap.yaml b/Api.Data.SqLite/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.SqLite/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.SqLite/.kubernetes/data-pvc.yaml b/Api.Data.SqLite/.kubernetes/data-pvc.yaml new file mode 100644 index 00000000..0b9d4415 --- /dev/null +++ b/Api.Data.SqLite/.kubernetes/data-pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %SERVICE_NAME%-data-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: %SERVICE_NAME%-data-storage-class + resources: + requests: + storage: %DATA_SIZE% \ No newline at end of file diff --git a/Api.Data.SqLite/.kubernetes/data-storageclass.yaml b/Api.Data.SqLite/.kubernetes/data-storageclass.yaml new file mode 100644 index 00000000..47530cdc --- /dev/null +++ b/Api.Data.SqLite/.kubernetes/data-storageclass.yaml @@ -0,0 +1,10 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: %SERVICE_NAME%-data-storage-class +provisioner: kubernetes.io/azure-disk +parameters: + storageaccounttype: Standard_LRS + kind: Managed +reclaimPolicy: Retain +volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/Api.Data.SqLite/.kubernetes/deployment.yaml b/Api.Data.SqLite/.kubernetes/deployment.yaml new file mode 100644 index 00000000..354c85fb --- /dev/null +++ b/Api.Data.SqLite/.kubernetes/deployment.yaml @@ -0,0 +1,85 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/data + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.SqLite/.kubernetes/service.yaml b/Api.Data.SqLite/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.SqLite/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Properties/DoNotParallelize.cs b/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Tests.Api.Data.SqLite.csproj b/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Tests.Api.Data.SqLite.csproj new file mode 100644 index 00000000..0ebcb00b --- /dev/null +++ b/Api.Data.SqLite/.tests/Tests.Api.Data.SqLite/Tests.Api.Data.SqLite.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj b/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj new file mode 100644 index 00000000..796675d7 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite.Models/Api.Data.SqLite.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..0898a54e --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.SqLite.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/Example.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/Example.cs new file mode 100644 index 00000000..8ec5fc89 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqLite.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatable.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatable.cs new file mode 100644 index 00000000..5674fe49 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqLite.Models; + +/// +/// Example. +/// +public class ExampleCreatable : BaseEntityCreatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatableAndEditable.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatableAndEditable.cs new file mode 100644 index 00000000..b79faaf8 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleCreatableAndEditable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqLite.Models; + +/// +/// Example. +/// +public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleDeletable.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleDeletable.cs new file mode 100644 index 00000000..ea738af4 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleDeletable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqLite.Models; + +/// +/// Example. +/// +public class ExampleDeletable : BaseEntityDeletable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleUpdatable.cs b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleUpdatable.cs new file mode 100644 index 00000000..acf1a748 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite.Models/ExampleUpdatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqLite.Models; + +/// +/// Example. +/// +public class ExampleUpdatable : BaseEntityUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite.sln b/Api.Data.SqLite/Api.Data.SqLite.sln new file mode 100644 index 00000000..4466c201 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite.sln @@ -0,0 +1,149 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\data-pvc.yaml = .kubernetes\data-pvc.yaml + .kubernetes\data-storageclass.yaml = .kubernetes\data-storageclass.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqLite.Models", "Api.Data.SqLite.Models\Api.Data.SqLite.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqLite", "Api.Data.SqLite\Api.Data.SqLite.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.SqLite", ".tests\Tests.Api.Data.SqLite\Tests.Api.Data.SqLite.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqLite", "..\..\Nano.Library\Nano.Data.SqLite\Nano.Data.SqLite.csproj", "{0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.SqLite/Api.Data.SqLite/Api.Data.SqLite.csproj b/Api.Data.SqLite/Api.Data.SqLite/Api.Data.SqLite.csproj new file mode 100644 index 00000000..ac14956f --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Api.Data.SqLite.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreatableAndEditableController.cs new file mode 100644 index 00000000..23b765ca --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreatableAndEditableController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqLite.Models; +using Api.Data.SqLite.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqLite.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) + : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreateablesController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreateablesController.cs new file mode 100644 index 00000000..4e4a20a5 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleCreateablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqLite.Models; +using Api.Data.SqLite.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqLite.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreateablesController(ILogger logger, IRepository repository) + : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleDeletablesController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleDeletablesController.cs new file mode 100644 index 00000000..816442f3 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleDeletablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqLite.Models; +using Api.Data.SqLite.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqLite.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleDeletablesController(ILogger logger, IRepository repository) + : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleUpdatablesController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleUpdatablesController.cs new file mode 100644 index 00000000..4eb1e08b --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExampleUpdatablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqLite.Models; +using Api.Data.SqLite.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqLite.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleUpdatablesController(ILogger logger, IRepository repository) + : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExamplesController.cs b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExamplesController.cs new file mode 100644 index 00000000..c7d426ba --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqLite.Models; +using Api.Data.SqLite.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqLite.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs new file mode 100644 index 00000000..04319331 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqLite.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqLite.Data.Mappings; + +/// +/// Example Creatable And Updatable Mapping. +/// +public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableMapping.cs new file mode 100644 index 00000000..45a6bd26 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleCreatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqLite.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqLite.Data.Mappings; + +/// +/// Example Creatable Mapping. +/// +public class ExampleCreatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleDeletableMapping.cs new file mode 100644 index 00000000..0eee93ec --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleDeletableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqLite.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqLite.Data.Mappings; + +/// +/// Example Deletable Mapping. +/// +public class ExampleDeletableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..82a1f096 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqLite.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqLite.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleUpdatableMapping.cs new file mode 100644 index 00000000..3849ad5a --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Data/Mappings/ExampleUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqLite.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqLite.Data.Mappings; + +/// +/// Example Updatable Mapping. +/// +public class ExampleUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContext.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContext.cs new file mode 100644 index 00000000..9726d363 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.SqLite.Data; + +/// +public class SqLiteDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContextFactory.cs b/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContextFactory.cs new file mode 100644 index 00000000..ac245907 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Data/SqLiteDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.SqLite; + +namespace Api.Data.SqLite.Data; + +/// +public class SqLiteDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local b/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local new file mode 100644 index 00000000..28ad6e11 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.SqLite.dll"] \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260427141022_Initial.Designer.cs b/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260427141022_Initial.Designer.cs new file mode 100644 index 00000000..7416d3a1 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260427141022_Initial.Designer.cs @@ -0,0 +1,758 @@ +// +using System; +using Api.Data.SqLite.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.SqLite.Migrations +{ + [DbContext(typeof(SqLiteDbContext))] + [Migration("20260427141022_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.7"); + + modelBuilder.Entity("Api.Data.SqLite.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.SqLite.Models.ExampleCreatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatable"); + }); + + modelBuilder.Entity("Api.Data.SqLite.Models.ExampleCreatableAndEditable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatableAndEditable"); + }); + + modelBuilder.Entity("Api.Data.SqLite.Models.ExampleDeletable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleDeletable"); + }); + + modelBuilder.Entity("Api.Data.SqLite.Models.ExampleUpdatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleUpdatable"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FriendlyName") + .HasColumnType("TEXT"); + + b.Property("Xml") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EntityKey") + .HasColumnType("TEXT"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("NewValue") + .HasColumnType("TEXT"); + + b.Property("OldValue") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RevokedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260427141022_Initial.cs b/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260427141022_Initial.cs new file mode 100644 index 00000000..5096c776 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Migrations/20260427141022_Initial.cs @@ -0,0 +1,668 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.SqLite.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + CreatedBy = table.Column(type: "TEXT", maxLength: 256, nullable: false), + EntityKey = table.Column(type: "TEXT", nullable: false), + EntitySetName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EntityTypeName = table.Column(type: "TEXT", maxLength: 256, nullable: false), + EntityState = table.Column(type: "INTEGER", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "TEXT", maxLength: 256, nullable: true), + IsDeleted = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + FriendlyName = table.Column(type: "TEXT", nullable: true), + Xml = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + IsActive = table.Column(type: "INTEGER", nullable: false, defaultValue: true), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleCreatable", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleCreatable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleCreatableAndEditable", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleCreatableAndEditable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleDeletable", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleDeletable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleUpdatable", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleUpdatable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ParentId = table.Column(type: "TEXT", nullable: false), + PropertyName = table.Column(type: "TEXT", maxLength: 256, nullable: false), + RelationName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NewValue = table.Column(type: "TEXT", nullable: true), + OldValue = table.Column(type: "TEXT", nullable: true), + IsDeleted = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + IdentityUserId = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: false), + Hash = table.Column(type: "TEXT", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false), + RevokedAt = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + IdentityUserId = table.Column(type: "TEXT", nullable: false), + NewEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NewPhoneNumber = table.Column(type: "TEXT", maxLength: 20, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "TEXT", nullable: false), + ProviderKey = table.Column(type: "TEXT", nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + IdentityUserId = table.Column(type: "TEXT", nullable: false), + AppId = table.Column(type: "TEXT", maxLength: 256, nullable: false), + Value = table.Column(type: "TEXT", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Value = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ApiKeyId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: false), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ApiKeyId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_CreatedAt", + table: "ExampleCreatable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_IsDeleted", + table: "ExampleCreatable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_Name", + table: "ExampleCreatable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_CreatedAt", + table: "ExampleCreatableAndEditable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_IsDeleted", + table: "ExampleCreatableAndEditable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_Name", + table: "ExampleCreatableAndEditable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_CreatedAt", + table: "ExampleDeletable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_IsDeleted", + table: "ExampleDeletable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_Name", + table: "ExampleDeletable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_CreatedAt", + table: "ExampleUpdatable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_IsDeleted", + table: "ExampleUpdatable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_Name", + table: "ExampleUpdatable", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "ExampleCreatable"); + + migrationBuilder.DropTable( + name: "ExampleCreatableAndEditable"); + + migrationBuilder.DropTable( + name: "ExampleDeletable"); + + migrationBuilder.DropTable( + name: "ExampleUpdatable"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.SqLite/Api.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs b/Api.Data.SqLite/Api.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs new file mode 100644 index 00000000..71e2dd70 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs @@ -0,0 +1,755 @@ +// +using System; +using Api.Data.SqLite.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.SqLite.Migrations +{ + [DbContext(typeof(SqLiteDbContext))] + partial class SqLiteDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.7"); + + modelBuilder.Entity("Api.Data.SqLite.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.SqLite.Models.ExampleCreatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatable"); + }); + + modelBuilder.Entity("Api.Data.SqLite.Models.ExampleCreatableAndEditable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatableAndEditable"); + }); + + modelBuilder.Entity("Api.Data.SqLite.Models.ExampleDeletable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleDeletable"); + }); + + modelBuilder.Entity("Api.Data.SqLite.Models.ExampleUpdatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleUpdatable"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FriendlyName") + .HasColumnType("TEXT"); + + b.Property("Xml") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EntityKey") + .HasColumnType("TEXT"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("NewValue") + .HasColumnType("TEXT"); + + b.Property("OldValue") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RevokedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SqLite/Api.Data.SqLite/Program.cs b/Api.Data.SqLite/Api.Data.SqLite/Program.cs new file mode 100644 index 00000000..933859f3 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.SqLite.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.SqLite; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.SqLite/Api.Data.SqLite/Properties/InternalsVisibleTo.cs b/Api.Data.SqLite/Api.Data.SqLite/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..13fd80d2 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.SqLite")] \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json new file mode 100644 index 00000000..8927ff99 --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Development.json @@ -0,0 +1,5 @@ +{ + "Data": { + "StartupAction": "Migrate" + } +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Production.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.Staging.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.SqLite/Api.Data.SqLite/appsettings.json b/Api.Data.SqLite/Api.Data.SqLite/appsettings.json new file mode 100644 index 00000000..496df4fb --- /dev/null +++ b/Api.Data.SqLite/Api.Data.SqLite/appsettings.json @@ -0,0 +1,39 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + }, + "Documentation": { + "Name": "Application" + } + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": "Data Source=/mnt/data/nanoDb.sqlite", + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } +} \ No newline at end of file diff --git a/Api.Data.SqLite/Dockerfile b/Api.Data.SqLite/Dockerfile new file mode 100644 index 00000000..8c1d942b --- /dev/null +++ b/Api.Data.SqLite/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.SqLite.dll"] \ No newline at end of file diff --git a/Api.Data.SqLite/LICENSE b/Api.Data.SqLite/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.SqLite/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.SqLite/README.md b/Api.Data.SqLite/README.md new file mode 100644 index 00000000..0884a1cc --- /dev/null +++ b/Api.Data.SqLite/README.md @@ -0,0 +1,167 @@ +# Api.Data.SqLite + +> _Nano API application with sqlite data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from +the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including [Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models), [Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings), +and the [Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context). + +Additionally, the example shows how Nano [Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories) works along with the corresponding +entity controllers. For more information on controllers and how they are connected with entity models, see [Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#controllers). + +A data health check is configured to target the database. +Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#health-checks)**. + +Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed +here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing +the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. + +> 📖 Learn more about **[Nano.Data.SqLite](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqLite/README.md#nanodatamysql)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +Also, an initial migration has been added to the project. + +```powershell +dotnet ef migrations add Initial --project Api.Data.SqLite +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "UseLazyLoading": false, + "StartupAction": "None", + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": "Data Source=/data/nanoDb.sqlite", + "Repository": { + "UseAutoSave": false, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } +} +``` + +...and `appsettings.Development.json` + +```json +"Data": { + "StartupAction": "Migrate" +} +``` + +## Docker Compose +Added SqLite as a service dependency in `docker-compose.yml`. + +```yaml +services: + api.data.sqlite: + volumes: + - ./bin/data:/mnt/data +``` + +## Kubernetes +Added two additional kubernetes templates, `storageclass.yaml` and `pvc.yaml`, for dynamically manage and creating the disk for the SqLite database. + +Also, updated `deployment.yaml` adding the volumes and volume mounts. + +```json +spec: + template: + spec: + containers: + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/data + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SQL_NAME: nanoDb + SQL_SIZE: 10Gi + SQL_CONNECTIONSTRING: "Data Source=/mnt/data/{{ env.nanoDb }}.sqlite" +``` + +Additionally, this step has been added to ensure database migrations are applied. + +```yaml +- name: Database Migration + shell: pwsh + run: | + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; +``` + +Deployment commands have also been updated to apply each of the new Kubernetes templates. + +```powershell +Get-Content .kubernetes/{resource-name}.yaml ` + | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` + | Set-Content .kubernetes/{resource-name}.tmp.yaml; + +sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; +``` diff --git a/Api.Data.SqLite/icon.png b/Api.Data.SqLite/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/.docker/docker-compose.yml b/Api.Data.SqlServer.Spatial/.docker/docker-compose.yml new file mode 100644 index 00000000..82e79e94 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.docker/docker-compose.yml @@ -0,0 +1,30 @@ +services: + api.data.sqlserver.spatial: + image: api.data.sqlserver.spatial + hostname: api-data-sqlserver-spatial + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.SqlServer.Spatial + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - 1433:1433 + networks: + - network + environment: + SA_PASSWORD: myPassword_123 + ACCEPT_EULA: Y + MSSQL_PID: Developer + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.SqlServer.Spatial/.dockerignore b/Api.Data.SqlServer.Spatial/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.SqlServer.Spatial/.github/config/slack.yml b/Api.Data.SqlServer.Spatial/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml b/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..e7532a1d --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.github/workflows/build-and-deploy.yml @@ -0,0 +1,254 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.SqlServer.Spatial + IMAGE_NAME: api.data.sqlserver.spatial + SERVICE_NAME: api-data-sqlserver-spatial + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-sqlserver-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "1433" + $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y mssql-tools unixodbc-dev + + $loginExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" + + if ($loginExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" + }; + + $userExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" + + if ($userExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; + ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; + ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" + }; + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_USER;Password=$env:SQL_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/.gitignore b/Api.Data.SqlServer.Spatial/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/auth-sql-secret.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/autoscaler.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.SqlServer.Spatial/.kubernetes/service.yaml b/Api.Data.SqlServer.Spatial/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Properties/DoNotParallelize.cs b/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Tests.Api.Data.SqlServer.Spatial.csproj b/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Tests.Api.Data.SqlServer.Spatial.csproj new file mode 100644 index 00000000..edb960c3 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/.tests/Tests.Api.Data.SqlServer.Spatial/Tests.Api.Data.SqlServer.Spatial.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj new file mode 100644 index 00000000..8c31e7ba --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Api.Data.SqlServer.Spatial.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..ad1ace22 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; +using NetTopologySuite.Geometries; + +namespace Api.Data.SqlServer.Spatial.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Point. + /// + public virtual Point? Point { get; set; } + + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (this.Point != null) + { + expression + .IsWithinDistance(nameof(Example.Point), this.Point, 10000); + } + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Example.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Example.cs new file mode 100644 index 00000000..8cd0560e --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.Models/Example.cs @@ -0,0 +1,22 @@ +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; +using NetTopologySuite.Geometries; + +namespace Api.Data.SqlServer.Spatial.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Point. + /// + [Required] + public virtual Point Point { get; set; } = null!; + + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.sln b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.sln new file mode 100644 index 00000000..1359bc5e --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqlServer.Spatial.Models", "Api.Data.SqlServer.Spatial.Models\Api.Data.SqlServer.Spatial.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqlServer.Spatial", "Api.Data.SqlServer.Spatial\Api.Data.SqlServer.Spatial.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.SqlServer.Spatial", ".tests\Tests.Api.Data.SqlServer.Spatial\Tests.Api.Data.SqlServer.Spatial.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqlServer", "..\..\Nano.Library\Nano.Data.SqlServer\Nano.Data.SqlServer.csproj", "{FE8B04D5-8AED-F023-588B-65947B83FDF5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {FE8B04D5-8AED-F023-588B-65947B83FDF5} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.csproj b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.csproj new file mode 100644 index 00000000..7ca03113 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Controllers/ExamplesController.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Controllers/ExamplesController.cs new file mode 100644 index 00000000..a0ed166d --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqlServer.Spatial.Models; +using Api.Data.SqlServer.Spatial.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqlServer.Spatial.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/Mappings/ExampleMapping.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..5552f03c --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,32 @@ +using System; +using Api.Data.SqlServer.Spatial.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqlServer.Spatial.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Point) + .HasColumnType("geography") + .IsRequired(); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContext.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContext.cs new file mode 100644 index 00000000..3bc5cbf4 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.SqlServer.Spatial.Data; + +/// +public class SqlServerDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContextFactory.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContextFactory.cs new file mode 100644 index 00000000..f9b9b8b9 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Data/SqlServerDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.SqlServer; + +namespace Api.Data.SqlServer.Spatial.Data; + +/// +public class SqlServerDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local new file mode 100644 index 00000000..cb81de7f --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.SqlServer.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.Designer.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.Designer.cs new file mode 100644 index 00000000..946d8122 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.Designer.cs @@ -0,0 +1,658 @@ +// +using System; +using Api.Data.SqlServer.Spatial.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; + +#nullable disable + +namespace Api.Data.SqlServer.Spatial.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + [Migration("20260415151456_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.SqlServer.Spatial.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Point") + .IsRequired() + .HasColumnType("geography"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("nvarchar(max)"); + + b.Property("Xml") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityKey") + .HasColumnType("uniqueidentifier"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("nvarchar(max)"); + + b.Property("OldValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(450)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetimeoffset"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.cs new file mode 100644 index 00000000..5340a112 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151456_Initial.cs @@ -0,0 +1,546 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using NetTopologySuite.Geometries; + +#nullable disable + +namespace Api.Data.SqlServer.Spatial.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + CreatedBy = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EntityKey = table.Column(type: "uniqueidentifier", nullable: false), + EntitySetName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EntityTypeName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + FriendlyName = table.Column(type: "nvarchar(max)", nullable: true), + Xml = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(450)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Point = table.Column(type: "geography", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ParentId = table.Column(type: "uniqueidentifier", nullable: false), + PropertyName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + RelationName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NewValue = table.Column(type: "nvarchar(max)", nullable: true), + OldValue = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Hash = table.Column(type: "nvarchar(max)", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false), + RevokedAt = table.Column(type: "datetimeoffset", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + NewEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NewPhoneNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + AppId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Value = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "datetimeoffset", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(450)", nullable: false), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true, + filter: "[Email] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true, + filter: "[PhoneNumber] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.Designer.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.Designer.cs new file mode 100644 index 00000000..b5459320 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.Designer.cs @@ -0,0 +1,658 @@ +// +using System; +using Api.Data.SqlServer.Spatial.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; + +#nullable disable + +namespace Api.Data.SqlServer.Spatial.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + [Migration("20260415151538_AddedSpatialIndex")] + partial class AddedSpatialIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.SqlServer.Spatial.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Point") + .IsRequired() + .HasColumnType("geography"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("nvarchar(max)"); + + b.Property("Xml") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityKey") + .HasColumnType("uniqueidentifier"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("nvarchar(max)"); + + b.Property("OldValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(450)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetimeoffset"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.cs new file mode 100644 index 00000000..6908b76e --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/20260415151538_AddedSpatialIndex.cs @@ -0,0 +1,25 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.SqlServer.Spatial.Migrations +{ + /// + public partial class AddedSpatialIndex : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder + .Sql(@"CREATE SPATIAL INDEX IX_Example_Point + ON Example(Point) + USING GEOGRAPHY_GRID"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/SqlServerDbContextModelSnapshot.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/SqlServerDbContextModelSnapshot.cs new file mode 100644 index 00000000..ac476593 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Migrations/SqlServerDbContextModelSnapshot.cs @@ -0,0 +1,655 @@ +// +using System; +using Api.Data.SqlServer.Spatial.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; + +#nullable disable + +namespace Api.Data.SqlServer.Spatial.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + partial class SqlServerDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.SqlServer.Spatial.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("Point") + .IsRequired() + .HasColumnType("geography"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("nvarchar(max)"); + + b.Property("Xml") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityKey") + .HasColumnType("uniqueidentifier"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("nvarchar(max)"); + + b.Property("OldValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(450)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetimeoffset"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Program.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Program.cs new file mode 100644 index 00000000..f044c3d8 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.SqlServer.Spatial.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.SqlServer; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Properties/InternalsVisibleTo.cs b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..1557b1b7 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.SqlServer.Spatial")] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Development.json b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Development.json new file mode 100644 index 00000000..960866dc --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Production.json b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Staging.json b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.json b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.json new file mode 100644 index 00000000..b4341f5e --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Api.Data.SqlServer.Spatial/appsettings.json @@ -0,0 +1,20 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + }, + "Documentation": { + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/Dockerfile b/Api.Data.SqlServer.Spatial/Dockerfile new file mode 100644 index 00000000..1bc24530 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.SqlServer.Spatial.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/LICENSE b/Api.Data.SqlServer.Spatial/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.SqlServer.Spatial/README.md b/Api.Data.SqlServer.Spatial/README.md new file mode 100644 index 00000000..07691b06 --- /dev/null +++ b/Api.Data.SqlServer.Spatial/README.md @@ -0,0 +1,35 @@ +# Api.Data.SqlServer.Spatial + +> _Nano API application with sql server spatial data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Migrations](#migrations) + +## Summary +This application builds on **[Api.Data.SqlServer](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.SqlServer)**. Entity controllers have been simplified to +showcase spatial types; full controllers are unnecessary. + +The `Example` entity now includes a `Point` property from `NetTopologySuite`. A query criterion has been added to check whether points are within a 10,000 meter distance. The +entity mappings for this spatial property have also been configured. Otherwise, no other changes were made. + +> ⚠️ Be aware, SQL Server does not create spatial indexes automatically; they must be added manually in a migration. + +```csharp +migrationBuilder + .Sql(@"CREATE SPATIAL INDEX IX_Example_Point + ON Example(Point) + USING GEOGRAPHY_GRID"); +``` + +> 📖 Learn more about **[Nano.Data.SqlServer](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqlServer/README.md#nanodatamysql)**. diff --git a/Api.Data.SqlServer.Spatial/icon.png b/Api.Data.SqlServer.Spatial/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.SqlServer/.docker/docker-compose.yml b/Api.Data.SqlServer/.docker/docker-compose.yml new file mode 100644 index 00000000..33a18108 --- /dev/null +++ b/Api.Data.SqlServer/.docker/docker-compose.yml @@ -0,0 +1,30 @@ +services: + api.data.sqlserver: + image: api.data.sqlserver + hostname: api-data-sqlserver + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.SqlServer + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - 1433:1433 + networks: + - network + environment: + SA_PASSWORD: myPassword_123 + ACCEPT_EULA: Y + MSSQL_PID: Developer + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.SqlServer/.dockerignore b/Api.Data.SqlServer/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.SqlServer/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.SqlServer/.github/config/slack.yml b/Api.Data.SqlServer/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.SqlServer/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml b/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..370b8153 --- /dev/null +++ b/Api.Data.SqlServer/.github/workflows/build-and-deploy.yml @@ -0,0 +1,254 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.SqlServer + IMAGE_NAME: api.data.sqlserver + SERVICE_NAME: api-data-sqlserver + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-sqlserver-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "1433" + $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y mssql-tools unixodbc-dev + + $loginExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" + + if ($loginExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" + }; + + $userExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" + + if ($userExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; + ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; + ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" + }; + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_USER;Password=$env:SQL_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.SqlServer/.gitignore b/Api.Data.SqlServer/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.SqlServer/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.SqlServer/.kubernetes/auth-sql-secret.yaml b/Api.Data.SqlServer/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.SqlServer/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.SqlServer/.kubernetes/autoscaler.yaml b/Api.Data.SqlServer/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.SqlServer/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.SqlServer/.kubernetes/configmap.yaml b/Api.Data.SqlServer/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.SqlServer/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.SqlServer/.kubernetes/deployment.yaml b/Api.Data.SqlServer/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.SqlServer/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.SqlServer/.kubernetes/service.yaml b/Api.Data.SqlServer/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.SqlServer/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Properties/DoNotParallelize.cs b/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Tests.Api.Data.SqlServer.csproj b/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Tests.Api.Data.SqlServer.csproj new file mode 100644 index 00000000..33f34658 --- /dev/null +++ b/Api.Data.SqlServer/.tests/Tests.Api.Data.SqlServer/Tests.Api.Data.SqlServer.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj new file mode 100644 index 00000000..8c31e7ba --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Api.Data.SqlServer.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..0129030d --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.SqlServer.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + /// Name. + /// + public virtual string? Name { get; set; } + + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + var expression = expressions.FirstOrDefault() ?? new CriteriaExpression(); + + if (!string.IsNullOrEmpty(this.Name)) + { + expression + .StartsWith(nameof(Example.Name), this.Name); + } + + expressions + .Add(expression); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/Example.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Example.cs new file mode 100644 index 00000000..0c053260 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer.Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqlServer.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatable.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatable.cs new file mode 100644 index 00000000..a3fe275d --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqlServer.Models; + +/// +/// Example. +/// +public class ExampleCreatable : BaseEntityCreatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatableAndEditable.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatableAndEditable.cs new file mode 100644 index 00000000..6a459d38 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleCreatableAndEditable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqlServer.Models; + +/// +/// Example. +/// +public class ExampleCreatableAndEditable : BaseEntityCreatableAndUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleDeletable.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleDeletable.cs new file mode 100644 index 00000000..8c458f93 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleDeletable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqlServer.Models; + +/// +/// Example. +/// +public class ExampleDeletable : BaseEntityDeletable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleUpdatable.cs b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleUpdatable.cs new file mode 100644 index 00000000..ce1959ab --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer.Models/ExampleUpdatable.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Api.Data.SqlServer.Models; + +/// +/// Example. +/// +public class ExampleUpdatable : BaseEntityUpdatable +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer.sln b/Api.Data.SqlServer/Api.Data.SqlServer.sln new file mode 100644 index 00000000..ba1df312 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqlServer.Models", "Api.Data.SqlServer.Models\Api.Data.SqlServer.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.SqlServer", "Api.Data.SqlServer\Api.Data.SqlServer.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.SqlServer", ".tests\Tests.Api.Data.SqlServer\Tests.Api.Data.SqlServer.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqlServer", "..\..\Nano.Library\Nano.Data.SqlServer\Nano.Data.SqlServer.csproj", "{FE8B04D5-8AED-F023-588B-65947B83FDF5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {FE8B04D5-8AED-F023-588B-65947B83FDF5} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Api.Data.SqlServer.csproj b/Api.Data.SqlServer/Api.Data.SqlServer/Api.Data.SqlServer.csproj new file mode 100644 index 00000000..56bec247 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Api.Data.SqlServer.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreatableAndEditableController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreatableAndEditableController.cs new file mode 100644 index 00000000..8f509aa5 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreatableAndEditableController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqlServer.Models; +using Api.Data.SqlServer.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqlServer.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreatableAndEditableController(ILogger logger, IRepository repository) + : BaseEntityCreatableAndEditableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreateablesController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreateablesController.cs new file mode 100644 index 00000000..7cf1f57b --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleCreateablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqlServer.Models; +using Api.Data.SqlServer.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqlServer.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleCreateablesController(ILogger logger, IRepository repository) + : BaseEntityCreatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleDeletablesController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleDeletablesController.cs new file mode 100644 index 00000000..4fdeda31 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleDeletablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqlServer.Models; +using Api.Data.SqlServer.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqlServer.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleDeletablesController(ILogger logger, IRepository repository) + : BaseEntityDeletableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleUpdatablesController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleUpdatablesController.cs new file mode 100644 index 00000000..3ea0f3c6 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExampleUpdatablesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqlServer.Models; +using Api.Data.SqlServer.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqlServer.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExampleUpdatablesController(ILogger logger, IRepository repository) + : BaseEntityUpdatableController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExamplesController.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExamplesController.cs new file mode 100644 index 00000000..279ec99b --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.SqlServer.Models; +using Api.Data.SqlServer.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.SqlServer.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs new file mode 100644 index 00000000..67adeef4 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableAndUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqlServer.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqlServer.Data.Mappings; + +/// +/// Example Creatable And Updatable Mapping. +/// +public class ExampleCreatableAndUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableMapping.cs new file mode 100644 index 00000000..66317926 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleCreatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqlServer.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqlServer.Data.Mappings; + +/// +/// Example Creatable Mapping. +/// +public class ExampleCreatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleDeletableMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleDeletableMapping.cs new file mode 100644 index 00000000..c35832c9 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleDeletableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqlServer.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqlServer.Data.Mappings; + +/// +/// Example Deletable Mapping. +/// +public class ExampleDeletableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..c840122b --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqlServer.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqlServer.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleUpdatableMapping.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleUpdatableMapping.cs new file mode 100644 index 00000000..2183ff74 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Data/Mappings/ExampleUpdatableMapping.cs @@ -0,0 +1,26 @@ +using System; +using Api.Data.SqlServer.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.SqlServer.Data.Mappings; + +/// +/// Example Updatable Mapping. +/// +public class ExampleUpdatableMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContext.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContext.cs new file mode 100644 index 00000000..3c12e60e --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.SqlServer.Data; + +/// +public class SqlServerDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContextFactory.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContextFactory.cs new file mode 100644 index 00000000..2d206aea --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Data/SqlServerDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.SqlServer; + +namespace Api.Data.SqlServer.Data; + +/// +public class SqlServerDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local b/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local new file mode 100644 index 00000000..8297641c --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.Designer.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.Designer.cs new file mode 100644 index 00000000..770c02a9 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.Designer.cs @@ -0,0 +1,773 @@ +// +using System; +using Api.Data.SqlServer.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + [Migration("20260415151054_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.SqlServer.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleCreatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatable"); + }); + + modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleCreatableAndEditable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatableAndEditable"); + }); + + modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleDeletable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleDeletable"); + }); + + modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleUpdatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleUpdatable"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("nvarchar(max)"); + + b.Property("Xml") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityKey") + .HasColumnType("uniqueidentifier"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("nvarchar(max)"); + + b.Property("OldValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(450)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetimeoffset"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.cs new file mode 100644 index 00000000..fa54f6f7 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/20260415151054_Initial.cs @@ -0,0 +1,672 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.SqlServer.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + CreatedBy = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EntityKey = table.Column(type: "uniqueidentifier", nullable: false), + EntitySetName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EntityTypeName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + FriendlyName = table.Column(type: "nvarchar(max)", nullable: true), + Xml = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(450)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleCreatable", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleCreatable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleCreatableAndEditable", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleCreatableAndEditable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleDeletable", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleDeletable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ExampleUpdatable", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleUpdatable", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ParentId = table.Column(type: "uniqueidentifier", nullable: false), + PropertyName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + RelationName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NewValue = table.Column(type: "nvarchar(max)", nullable: true), + OldValue = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Hash = table.Column(type: "nvarchar(max)", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false), + RevokedAt = table.Column(type: "datetimeoffset", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + NewEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NewPhoneNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + AppId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Value = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "datetimeoffset", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(450)", nullable: false), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true, + filter: "[Email] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true, + filter: "[PhoneNumber] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_CreatedAt", + table: "ExampleCreatable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_IsDeleted", + table: "ExampleCreatable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatable_Name", + table: "ExampleCreatable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_CreatedAt", + table: "ExampleCreatableAndEditable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_IsDeleted", + table: "ExampleCreatableAndEditable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleCreatableAndEditable_Name", + table: "ExampleCreatableAndEditable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_CreatedAt", + table: "ExampleDeletable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_IsDeleted", + table: "ExampleDeletable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleDeletable_Name", + table: "ExampleDeletable", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_CreatedAt", + table: "ExampleUpdatable", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_IsDeleted", + table: "ExampleUpdatable", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleUpdatable_Name", + table: "ExampleUpdatable", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "ExampleCreatable"); + + migrationBuilder.DropTable( + name: "ExampleCreatableAndEditable"); + + migrationBuilder.DropTable( + name: "ExampleDeletable"); + + migrationBuilder.DropTable( + name: "ExampleUpdatable"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs new file mode 100644 index 00000000..daf3a185 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs @@ -0,0 +1,770 @@ +// +using System; +using Api.Data.SqlServer.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + partial class SqlServerDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.SqlServer.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleCreatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatable"); + }); + + modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleCreatableAndEditable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleCreatableAndEditable"); + }); + + modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleDeletable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleDeletable"); + }); + + modelBuilder.Entity("Api.Data.SqlServer.Models.ExampleUpdatable", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("ExampleUpdatable"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("nvarchar(max)"); + + b.Property("Xml") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityKey") + .HasColumnType("uniqueidentifier"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("nvarchar(max)"); + + b.Property("OldValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(450)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetimeoffset"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Program.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Program.cs new file mode 100644 index 00000000..d2643c8a --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.SqlServer.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.SqlServer; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/Properties/InternalsVisibleTo.cs b/Api.Data.SqlServer/Api.Data.SqlServer/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..2b16073e --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.SqlServer")] \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Development.json b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Development.json new file mode 100644 index 00000000..960866dc --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Production.json b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Staging.json b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.json b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.json new file mode 100644 index 00000000..2871c661 --- /dev/null +++ b/Api.Data.SqlServer/Api.Data.SqlServer/appsettings.json @@ -0,0 +1,39 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + }, + "Documentation": { + "Name": "Application" + } + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } +} \ No newline at end of file diff --git a/Api.Data.SqlServer/Dockerfile b/Api.Data.SqlServer/Dockerfile new file mode 100644 index 00000000..e140c4f8 --- /dev/null +++ b/Api.Data.SqlServer/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Api.Data.SqlServer/LICENSE b/Api.Data.SqlServer/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.SqlServer/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.SqlServer/README.md b/Api.Data.SqlServer/README.md new file mode 100644 index 00000000..c1286d17 --- /dev/null +++ b/Api.Data.SqlServer/README.md @@ -0,0 +1,223 @@ +# Api.Data.SqlServer + +> _Nano API application with sql server data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a test controller that inherits from +the Nano `BaseEntityControllerr`. The available entity endpoints are inherited, and no additional endpoints has been added. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings)**, +and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context)**. + +Additionally, the example shows how Nano **[Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories)** works along with the corresponding +entity controllers. For more information on controllers and how they are connected with entity models, see **[Nano Entity Controllers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#controllers)**. + +A data health check is configured to target the database. +Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#health-checks)**. + +Also, API documentation has been configured, in order to easier see which endpoints are available. It can be accessed +here: **[http://localhost:8080/docs](http://localhost:8080/docs)**. + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +Additionally, controllers have been implemented to demonstrate controllers for creatable, updatable, creatable-and-updatable, and deletable entities. When viewing +the API documentation, observe how the available endpoints differ depending on the capabilities supported by each controller. + +> 📖 Learn more about **[Nano.Data.SqlServer](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqlServer/README.md#nanodatamysql)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +Also, an initial migration has been added to the project. + +```powershell +dotnet ef migrations add Initial --project Api.Data.SqlServer +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "UseLazyLoading": false, + "StartupAction": "None", + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": false, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } +} +``` + +...and `appsettings.Development.json` + +```json +"Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" +} +``` + +## Docker Compose +Added Sql Server as a service dependency in `docker-compose.yml`. + +```yaml +services: + api.data.sqlserver: + depends_on: + - database + + database: + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - 1433:1433 + networks: + - network + environment: + SA_PASSWORD: myPassword_123 + ACCEPT_EULA: Y + MSSQL_PID: Developer +``` + +## Kubernetes +Added the `%SERVICE_NAME%-secret` for the connectionstring to the `deployment.yaml`. + +```json +spec: + template: + spec: + containers: + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SQL_NAME: nanoDb + SQL_USER: api-data-sqlserver-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +``` + +Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. + +```yaml +- name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "1433" + $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y mssql-tools unixodbc-dev + + $loginExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" + + if ($loginExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" + }; + + $userExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" + + if ($userExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; + ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; + ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" + }; +``` + +Last, the application connectionstring must be added in a secret in Kuberntes. The `Kubernetes Deploy` step has been updated with the following. + +```yaml +sudo kubectl create secret generic $env:SERVICE_NAME-data-secret ` --from-literal=data-connectionstring=$env:DATA_CONNECTIONSTRING --save-config --dry-run=client -o yaml | sudo kubectl apply -f -; +if ($LastExitCode -ne 0) +{ + throw "error"; +}; +``` diff --git a/Api.Data.SqlServer/icon.png b/Api.Data.SqlServer/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Data.Triggers/.docker/docker-compose.yml b/Api.Data.Triggers/.docker/docker-compose.yml new file mode 100644 index 00000000..1bd3d4c3 --- /dev/null +++ b/Api.Data.Triggers/.docker/docker-compose.yml @@ -0,0 +1,32 @@ +services: + api.data.repository.triggers: + image: api.data.repository.triggers + hostname: api-data-triggers + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Data.Triggers + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + driver: bridge diff --git a/Api.Data.Triggers/.dockerignore b/Api.Data.Triggers/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Data.Triggers/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Data.Triggers/.github/config/slack.yml b/Api.Data.Triggers/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Data.Triggers/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Data.Triggers/.github/workflows/build-and-deploy.yml b/Api.Data.Triggers/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..0cbf7483 --- /dev/null +++ b/Api.Data.Triggers/.github/workflows/build-and-deploy.yml @@ -0,0 +1,235 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Data.Triggers + IMAGE_NAME: api.data.triggers + SERVICE_NAME: api-data-triggers + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_KUBERNETES_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Data.Triggers/.gitignore b/Api.Data.Triggers/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Data.Triggers/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Data.Triggers/.kubernetes/auth-sql-secret.yaml b/Api.Data.Triggers/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Api.Data.Triggers/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Api.Data.Triggers/.kubernetes/autoscaler.yaml b/Api.Data.Triggers/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Data.Triggers/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Data.Triggers/.kubernetes/configmap.yaml b/Api.Data.Triggers/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Data.Triggers/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Data.Triggers/.kubernetes/deployment.yaml b/Api.Data.Triggers/.kubernetes/deployment.yaml new file mode 100644 index 00000000..7e8688ef --- /dev/null +++ b/Api.Data.Triggers/.kubernetes/deployment.yaml @@ -0,0 +1,84 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Data.Triggers/.kubernetes/service.yaml b/Api.Data.Triggers/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Data.Triggers/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Properties/DoNotParallelize.cs b/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Tests.Api.Data.Triggers.csproj b/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Tests.Api.Data.Triggers.csproj new file mode 100644 index 00000000..5e2fd720 --- /dev/null +++ b/Api.Data.Triggers/.tests/Tests.Api.Data.Triggers/Tests.Api.Data.Triggers.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj b/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj new file mode 100644 index 00000000..82be9b94 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers.Models/Api.Data.Triggers.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleQueryCriteria.cs b/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleQueryCriteria.cs new file mode 100644 index 00000000..e9546b7e --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleQueryCriteria.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.Triggers.Models.Criterias; + +/// +public class ExampleQueryCriteria : BaseQueryCriteria +{ + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleTriggerQueryCriteria.cs b/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleTriggerQueryCriteria.cs new file mode 100644 index 00000000..d401429f --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers.Models/Criterias/ExampleTriggerQueryCriteria.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using DynamicExpression; +using Nano.App.Api.Controllers.Criteria; + +namespace Api.Data.Triggers.Models.Criterias; + +/// +public class ExampleTriggerQueryCriteria : BaseQueryCriteria +{ + /// + public override IList GetExpressions() + { + var expressions = base.GetExpressions(); + + return expressions; + } +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/Example.cs b/Api.Data.Triggers/Api.Data.Triggers.Models/Example.cs new file mode 100644 index 00000000..4be5377c --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers.Models/Example.cs @@ -0,0 +1,22 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Triggers.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + [Required] + public virtual string Name { get; set; } = null!; + + /// + /// Updated At. + /// + public virtual DateTimeOffset? UpdatedAt { get; set; } +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.Models/ExampleTrigger.cs b/Api.Data.Triggers/Api.Data.Triggers.Models/ExampleTrigger.cs new file mode 100644 index 00000000..bcb7c553 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers.Models/ExampleTrigger.cs @@ -0,0 +1,22 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Nano.Data.Abstractions.Models; + +namespace Api.Data.Triggers.Models; + +/// +/// Example Trigger. +/// +public class ExampleTrigger : BaseEntity +{ + /// + /// Example Id. + /// + [Required] + public virtual Guid ExampleId { get; set; } + + /// + /// Trigger. + /// + public virtual string Trigger { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers.sln b/Api.Data.Triggers/Api.Data.Triggers.sln new file mode 100644 index 00000000..a31fb6e1 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers.sln @@ -0,0 +1,148 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Triggers.Models", "Api.Data.Triggers.Models\Api.Data.Triggers.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Data.Triggers", "Api.Data.Triggers\Api.Data.Triggers.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Data.Triggers", ".tests\Tests.Api.Data.Triggers\Tests.Api.Data.Triggers.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Data.Triggers/Api.Data.Triggers/Api.Data.Triggers.csproj b/Api.Data.Triggers/Api.Data.Triggers/Api.Data.Triggers.csproj new file mode 100644 index 00000000..f1828c23 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Api.Data.Triggers.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExampleTriggersController.cs b/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExampleTriggersController.cs new file mode 100644 index 00000000..4166e77f --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExampleTriggersController.cs @@ -0,0 +1,15 @@ +using Api.Data.Triggers.Models; +using Api.Data.Triggers.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.Triggers.Controllers; + +/// +/// Controller with example triggers. +/// +/// The . +/// The . +public class ExampleTriggersController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExamplesController.cs b/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExamplesController.cs new file mode 100644 index 00000000..4476cba5 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Controllers/ExamplesController.cs @@ -0,0 +1,15 @@ +using Api.Data.Triggers.Models; +using Api.Data.Triggers.Models.Criterias; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Data.Abstractions; + +namespace Api.Data.Triggers.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IRepository repository) + : BaseEntityController(logger, repository); \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleMapping.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..c0849781 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,52 @@ +using System; +using Api.Data.Triggers.Data.Triggers; +using Api.Data.Triggers.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; +using Nano.Data.Mappings.Extensions; + +namespace Api.Data.Triggers.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + + builder + .Property(x => x.UpdatedAt); + + builder + .HasIndex(x => x.UpdatedAt); + + builder + .OnInserting(ExampleTriggers.Inserting); + + builder + .OnInserted(ExampleTriggers.Inserted); + + builder + .OnUpdating(ExampleTriggers.Updating); + + builder + .OnUpdated(ExampleTriggers.Updated); + + builder + .OnDeleting(ExampleTriggers.Deleting); + + builder + .OnDeleted(ExampleTriggers.Deleted); + } +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleTriggerMapping.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleTriggerMapping.cs new file mode 100644 index 00000000..7bc3a37c --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Data/Mappings/ExampleTriggerMapping.cs @@ -0,0 +1,28 @@ +using System; +using Api.Data.Triggers.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Api.Data.Triggers.Data.Mappings; + +/// +/// Example Trigger Mapping. +/// +public class ExampleTriggerMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Trigger) + .IsRequired(); + + builder + .Property(x => x.ExampleId) + .IsRequired(); + } +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContext.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContext.cs new file mode 100644 index 00000000..c85366c3 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Api.Data.Triggers.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContextFactory.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..a2ab5c49 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Api.Data.Triggers.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Data/Triggers/ExampleTriggers.cs b/Api.Data.Triggers/Api.Data.Triggers/Data/Triggers/ExampleTriggers.cs new file mode 100644 index 00000000..42b8da92 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Data/Triggers/ExampleTriggers.cs @@ -0,0 +1,72 @@ +using System; +using Api.Data.Triggers.Models; +using EntityFrameworkCore.Triggers; + +namespace Api.Data.Triggers.Data.Triggers; + +internal static class ExampleTriggers +{ + internal static Action> Inserting = x => + { + x.Entity.UpdatedAt = DateTimeOffset.UtcNow; + + x.Context + .Add(new ExampleTrigger + { + ExampleId = x.Entity.Id, + Trigger = "OnInserting" + }); + }; + + internal static Action> Inserted = x => + { + x.Context + .Add(new ExampleTrigger + { + ExampleId = x.Entity.Id, + Trigger = "OnInserted" + }); + }; + + internal static Action> Updating = x => + { + x.Entity.UpdatedAt = DateTimeOffset.UtcNow; + + x.Context + .Add(new ExampleTrigger + { + ExampleId = x.Entity.Id, + Trigger = "OnUpdating" + }); + }; + + internal static Action> Updated = x => + { + x.Context + .Add(new ExampleTrigger + { + ExampleId = x.Entity.Id, + Trigger = "OnUpdated" + }); + }; + + internal static Action> Deleting = x => + { + x.Context + .Add(new ExampleTrigger + { + ExampleId = x.Entity.Id, + Trigger = "OnDeleting" + }); + }; + + internal static Action> Deleted = x => + { + x.Context + .Add(new ExampleTrigger + { + ExampleId = x.Entity.Id, + Trigger = "OnDeleted" + }); + }; +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local b/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local new file mode 100644 index 00000000..b8b7fc64 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Data.Triggers.dll"] \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.Designer.cs b/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.Designer.cs new file mode 100644 index 00000000..030ada5a --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.Designer.cs @@ -0,0 +1,689 @@ +// +using System; +using Api.Data.Triggers.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Triggers.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260415151450_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Triggers.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.HasIndex("UpdatedAt"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.Triggers.Models.ExampleTrigger", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("ExampleId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Trigger") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleTrigger"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.cs b/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.cs new file mode 100644 index 00000000..aa043be6 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Migrations/20260415151450_Initial.cs @@ -0,0 +1,638 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Api.Data.Triggers.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + UpdatedAt = table.Column(type: "datetime(6)", nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "ExampleTrigger", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ExampleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Trigger = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_ExampleTrigger", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Example_UpdatedAt", + table: "Example", + column: "UpdatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleTrigger_CreatedAt", + table: "ExampleTrigger", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_ExampleTrigger_IsDeleted", + table: "ExampleTrigger", + column: "IsDeleted"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "ExampleTrigger"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Api.Data.Triggers/Api.Data.Triggers/Migrations/MySqlDbContextModelSnapshot.cs b/Api.Data.Triggers/Api.Data.Triggers/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..bef3c9d5 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,686 @@ +// +using System; +using Api.Data.Triggers.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Api.Data.Triggers.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Api.Data.Triggers.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.HasIndex("UpdatedAt"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Api.Data.Triggers.Models.ExampleTrigger", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("ExampleId") + .HasColumnType("char(36)"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Trigger") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.ToTable("ExampleTrigger"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Api.Data.Triggers/Api.Data.Triggers/Program.cs b/Api.Data.Triggers/Api.Data.Triggers/Program.cs new file mode 100644 index 00000000..80733e67 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Program.cs @@ -0,0 +1,13 @@ +using Api.Data.Triggers.Data; +using Nano.App.Api; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); diff --git a/Api.Data.Triggers/Api.Data.Triggers/Properties/InternalsVisibleTo.cs b/Api.Data.Triggers/Api.Data.Triggers/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..247e40b4 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Data.Triggers")] \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Production.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.Staging.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Data.Triggers/Api.Data.Triggers/appsettings.json b/Api.Data.Triggers/Api.Data.Triggers/appsettings.json new file mode 100644 index 00000000..d43a066a --- /dev/null +++ b/Api.Data.Triggers/Api.Data.Triggers/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Data": { + "ConnectionString": null + } +} \ No newline at end of file diff --git a/Api.Data.Triggers/Dockerfile b/Api.Data.Triggers/Dockerfile new file mode 100644 index 00000000..6f4ffa68 --- /dev/null +++ b/Api.Data.Triggers/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Data.Triggers.dll"] \ No newline at end of file diff --git a/Api.Data.Triggers/LICENSE b/Api.Data.Triggers/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Data.Triggers/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Data.Triggers/README.md b/Api.Data.Triggers/README.md new file mode 100644 index 00000000..02126d18 --- /dev/null +++ b/Api.Data.Triggers/README.md @@ -0,0 +1,26 @@ +# Api.Data.Triggers + +> _Nano API application with data triggers._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Data.MySql](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Data.MySql)**, but any data provider can be used to +demonstrate repository autosave. Entity controllers have been simplified to showcase triggers; full controllers are unnecessary. + +Triggers for `OnInserting`, `OnInserted`, `OnUpdating`, `OnUpdated`, `OnDeleting`, and `OnDeleted` have been configured in mappings for the `Example` entity model. Whenever +the `Example` entity is **added** or **updated**, the `Example.UpdatedAt` property is automatically set to `UtcNow`. Additionally, for each trigger execution, an +`ExampleTrigger` entity is created and stored. This serves as a record demonstrating that the trigger was invoked. + +> 📖 Learn more about **[Nano Data Triggers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#triggers)**. diff --git a/Api.Data.Triggers/icon.png b/Api.Data.Triggers/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Documentation.Csp/.docker/docker-compose.yml b/Api.Documentation.Csp/.docker/docker-compose.yml new file mode 100644 index 00000000..ec265b69 --- /dev/null +++ b/Api.Documentation.Csp/.docker/docker-compose.yml @@ -0,0 +1,20 @@ +services: + api.documentation.csp: + image: api.documentation.csp + hostname: api-documentation.csp + restart: on-failure + ports: + - 8080:8080 + - 4443:4443 + build: + context: ../Api.Documentation.Csp + dockerfile: "Dockerfile.Local" + volumes: + - ../:/root/.dotnet/https + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Documentation.Csp/.dockerignore b/Api.Documentation.Csp/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Documentation.Csp/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Documentation.Csp/.github/config/slack.yml b/Api.Documentation.Csp/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Documentation.Csp/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml b/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..00b3b84b --- /dev/null +++ b/Api.Documentation.Csp/.github/workflows/build-and-deploy.yml @@ -0,0 +1,196 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Documentation.Csp + IMAGE_NAME: api.documentation.csp + SERVICE_NAME: api-documentation-csp + SUB_DOMAIN_NAME: nano + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query [].name; + + $env:ROUTE_HOST_NAMES = @( + foreach ($zoneName in $zoneNames) { + @" + - $env:SUB_DOMAIN_NAME.$($zoneName) + "@ + } + ) -join "`n"; + + $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + + $command = @" + set -eu + + kubectl apply -f service.tmp.yaml || exit 1 + kubectl apply -f configmap.tmp.yaml || exit 2 + kubectl apply -f deployment.tmp.yaml || exit 3 + kubectl apply -f autoscaler.tmp.yaml || exit 4 + "@; + + $result = az aks command invoke ` + -g $env:AZURE_GROUP_KUBERNETES ` + -n $env:KUBERNETES_CLUSTER ` + --file .kubernetes/service.tmp.yaml ` + --file .kubernetes/configmap.tmp.yaml ` + --file .kubernetes/deployment.tmp.yaml ` + --file .kubernetes/autoscaler.tmp.yaml ` + --command $command ` + --output json | ConvertFrom-Json; + + echo $result; + + if ($result.exitCode -ne 0) + { + throw "error"; + } + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Documentation.Csp/.gitignore b/Api.Documentation.Csp/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Documentation.Csp/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Documentation.Csp/.kubernetes/autoscaler.yaml b/Api.Documentation.Csp/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Documentation.Csp/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Documentation.Csp/.kubernetes/configmap.yaml b/Api.Documentation.Csp/.kubernetes/configmap.yaml new file mode 100644 index 00000000..d13013d1 --- /dev/null +++ b/Api.Documentation.Csp/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Documentation.Csp/.kubernetes/deployment.yaml b/Api.Documentation.Csp/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Documentation.Csp/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Documentation.Csp/.kubernetes/service.yaml b/Api.Documentation.Csp/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Documentation.Csp/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Documentation.Csp/.tests/Tests.Api.Documentation.Csp/Properties/DoNotParallelize.cs b/Api.Documentation.Csp/.tests/Tests.Api.Documentation.Csp/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Documentation.Csp/.tests/Tests.Api.Documentation.Csp/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Documentation.Csp/.tests/Tests.Api.Documentation.Csp/Tests.Api.Documentation.Csp.csproj b/Api.Documentation.Csp/.tests/Tests.Api.Documentation.Csp/Tests.Api.Documentation.Csp.csproj new file mode 100644 index 00000000..680f17ff --- /dev/null +++ b/Api.Documentation.Csp/.tests/Tests.Api.Documentation.Csp/Tests.Api.Documentation.Csp.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Documentation.Csp/Api.Documentation.Csp.Models/Api.Documentation.Csp.Models.csproj b/Api.Documentation.Csp/Api.Documentation.Csp.Models/Api.Documentation.Csp.Models.csproj new file mode 100644 index 00000000..7a4d41f3 --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp.Models/Api.Documentation.Csp.Models.csproj @@ -0,0 +1,74 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Documentation.Csp/Api.Documentation.Csp.sln b/Api.Documentation.Csp/Api.Documentation.Csp.sln new file mode 100644 index 00000000..7f41e980 --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Documentation.Csp.Models", "Api.Documentation.Csp.Models\Api.Documentation.Csp.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Documentation.Csp", "Api.Documentation.Csp\Api.Documentation.Csp.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Documentation.Csp", ".tests\Tests.Api.Documentation.Csp\Tests.Api.Documentation.Csp.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/Api.Documentation.Csp.csproj b/Api.Documentation.Csp/Api.Documentation.Csp/Api.Documentation.Csp.csproj new file mode 100644 index 00000000..cfb4bb7c --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/Api.Documentation.Csp.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/Controllers/ExamplesController.cs b/Api.Documentation.Csp/Api.Documentation.Csp/Controllers/ExamplesController.cs new file mode 100644 index 00000000..24f1730a --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Documentation.Nonce.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Documentation Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("documentation")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task DocumentationAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("documentation"); + } +} \ No newline at end of file diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/Dockerfile.Local b/Api.Documentation.Csp/Api.Documentation.Csp/Dockerfile.Local new file mode 100644 index 00000000..8f4d7cbf --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 4443 + +ENTRYPOINT ["dotnet", "Api.Documentation.Csp.dll"] \ No newline at end of file diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/Program.cs b/Api.Documentation.Csp/Api.Documentation.Csp/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/Properties/InternalsVisibleTo.cs b/Api.Documentation.Csp/Api.Documentation.Csp/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..243d1ab5 --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Documentation.Csp")] \ No newline at end of file diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Development.json b/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Production.json b/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Staging.json b/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.json b/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.json new file mode 100644 index 00000000..e6c4ccc8 --- /dev/null +++ b/Api.Documentation.Csp/Api.Documentation.Csp/appsettings.json @@ -0,0 +1,29 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Documentation": { + "Name": "Application" + }, + "HttpPolicyHeaders": { + "Csp": { + "Scripts": { + "IsSelf": true + }, + "Styles": { + "IsSelf": true, + "Hashes": [ + "sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=" + ] + } + } + } + } +} \ No newline at end of file diff --git a/Api.Documentation.Csp/Dockerfile b/Api.Documentation.Csp/Dockerfile new file mode 100644 index 00000000..32d43b6b --- /dev/null +++ b/Api.Documentation.Csp/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Documentation.Csp.dll"] \ No newline at end of file diff --git a/Api.Documentation.Csp/LICENSE b/Api.Documentation.Csp/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Documentation.Csp/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Documentation.Csp/README.md b/Api.Documentation.Csp/README.md new file mode 100644 index 00000000..65025793 --- /dev/null +++ b/Api.Documentation.Csp/README.md @@ -0,0 +1,54 @@ +# Api.Documentation.Csp + +> _Nano API application with api documentation and CSP._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Documenation](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Documenation)**. + +This example shows using API documentation with strict CSP security. Run the solution and open [https://localhost:4443/docs](https://localhost:4443/docs) in your browser to +view the API documentation. + +Also a CSP hash has been added and the policy configured to allow inline styles for swagger. + +| Endpoint | Description | +| -------------------------------------------------- | -------------------- | +| `http://localhost:8080/api/examples/documentation` | Returns a `200 OK`. | + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +## Configuration + +```json +"App": { + "Documentation": { + "Name": "Application" + }, + "HttpPolicyHeaders": { + "Csp": { + "Scripts": { + "IsSelf": true + }, + "Styles": { + "IsSelf": true, + "Hashes": [ + "sha256-RL3ie0nH+Lzz2YNqQN83mnU0J1ot4QL7b99vMdIX99w=" + ] + } + } + } +} +``` diff --git a/Api.Documentation.Csp/icon.png b/Api.Documentation.Csp/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Documentation/.docker/docker-compose.yml b/Api.Documentation/.docker/docker-compose.yml new file mode 100644 index 00000000..80173a06 --- /dev/null +++ b/Api.Documentation/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.documentation: + image: api.documentation + hostname: api-documentation + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Documentation + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Documentation/.dockerignore b/Api.Documentation/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Documentation/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Documentation/.github/config/slack.yml b/Api.Documentation/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Documentation/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Documentation/.github/workflows/build-and-deploy.yml b/Api.Documentation/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..87545d98 --- /dev/null +++ b/Api.Documentation/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Documentation + IMAGE_NAME: api.documentation + SERVICE_NAME: api-documentation + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Documentation/.gitignore b/Api.Documentation/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Documentation/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Documentation/.kubernetes/autoscaler.yaml b/Api.Documentation/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Documentation/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Documentation/.kubernetes/configmap.yaml b/Api.Documentation/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Documentation/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Documentation/.kubernetes/deployment.yaml b/Api.Documentation/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Documentation/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Documentation/.kubernetes/service.yaml b/Api.Documentation/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Documentation/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Documentation/.tests/Tests.Api.Documentation/Properties/DoNotParallelize.cs b/Api.Documentation/.tests/Tests.Api.Documentation/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Documentation/.tests/Tests.Api.Documentation/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Documentation/.tests/Tests.Api.Documentation/Tests.Api.Documentation.csproj b/Api.Documentation/.tests/Tests.Api.Documentation/Tests.Api.Documentation.csproj new file mode 100644 index 00000000..f6d76b00 --- /dev/null +++ b/Api.Documentation/.tests/Tests.Api.Documentation/Tests.Api.Documentation.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj b/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Documentation/Api.Documentation.Models/Api.Documentation.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation.sln b/Api.Documentation/Api.Documentation.sln new file mode 100644 index 00000000..5317a173 --- /dev/null +++ b/Api.Documentation/Api.Documentation.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Documentation.Models", "Api.Documentation.Models\Api.Documentation.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Documentation", "Api.Documentation\Api.Documentation.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Documentation", ".tests\Tests.Api.Documentation\Tests.Api.Documentation.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Documentation/Api.Documentation/Api.Documentation.csproj b/Api.Documentation/Api.Documentation/Api.Documentation.csproj new file mode 100644 index 00000000..b0758e7f --- /dev/null +++ b/Api.Documentation/Api.Documentation/Api.Documentation.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/Controllers/ExamplesController.cs b/Api.Documentation/Api.Documentation/Controllers/ExamplesController.cs new file mode 100644 index 00000000..fe61e9d2 --- /dev/null +++ b/Api.Documentation/Api.Documentation/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Documentation.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Documentation Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("documentation")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task DocumentationAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("documentation"); + } +} \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/Dockerfile.Local b/Api.Documentation/Api.Documentation/Dockerfile.Local new file mode 100644 index 00000000..8304fa2a --- /dev/null +++ b/Api.Documentation/Api.Documentation/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Documentation.dll"] \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/Program.cs b/Api.Documentation/Api.Documentation/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Documentation/Api.Documentation/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Documentation/Api.Documentation/Properties/InternalsVisibleTo.cs b/Api.Documentation/Api.Documentation/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..d56f2a5b --- /dev/null +++ b/Api.Documentation/Api.Documentation/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Documentation")] \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/appsettings.Development.json b/Api.Documentation/Api.Documentation/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Documentation/Api.Documentation/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/appsettings.Production.json b/Api.Documentation/Api.Documentation/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Documentation/Api.Documentation/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/appsettings.Staging.json b/Api.Documentation/Api.Documentation/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Documentation/Api.Documentation/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Documentation/Api.Documentation/appsettings.json b/Api.Documentation/Api.Documentation/appsettings.json new file mode 100644 index 00000000..135418df --- /dev/null +++ b/Api.Documentation/Api.Documentation/appsettings.json @@ -0,0 +1,30 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Documentation": { + "Name": "Application", + "Description": "This is an example application", + "TermsOfService": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", + "Contact": { + "Name": "Nano Contributors", + "Email": "email@email.com", + "Url": "https://github.com/Nano-Core" + }, + "License": { + "Name": "MIT", + "Identifier": "MIT", + "Url": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE" + }, + "CspNonce": null, + "HideDefaultVersion": true + } + } +} \ No newline at end of file diff --git a/Api.Documentation/Dockerfile b/Api.Documentation/Dockerfile new file mode 100644 index 00000000..bc41ebbf --- /dev/null +++ b/Api.Documentation/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Documentation.dll"] \ No newline at end of file diff --git a/Api.Documentation/LICENSE b/Api.Documentation/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Documentation/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Documentation/README.md b/Api.Documentation/README.md new file mode 100644 index 00000000..49195e45 --- /dev/null +++ b/Api.Documentation/README.md @@ -0,0 +1,59 @@ +# Api.Documentation + +> _Nano API application with api documentation._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#gitHub-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example shows using API documentation for an Nano application. Run the solution and open +[http://localhost:8080/docs](http://localhost:8080/docs) in your browser to view the API documentation. + +You can experiment with the `HideDefaultVersion` setting. When set to `false`, Swagger displays both the non-versioned route and the versioned route +for the same example endpoint. When set to `true`, Swagger only displays the non-versioned route corresponding to the application's default version. + +| Endpoint | Description | +| -------------------------------------------------- | -------------------- | +| `http://localhost:8080/api/examples/documentation` | Returns a `200 OK`. | + +> 📖 Learn more about **[Nano API Documentation](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#documentation)**. + +## Configuration +```json +"App": { + "Documentation": { + "Name": "Application", + "Description": "This is an example application", + "TermsOfService": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE", + "Contact": { + "Name": "Nano Contributors", + "Email": "email@email.com", + "Url": "https://github.com/Nano-Core" + }, + "License": { + "Name": "MIT", + "Identifier": "MIT", + "Url": "https://github.com/Nano-Core/Nano.Library/blob/master/LICENSE" + }, + "CspNonce": "null, + "HideDefaultVersion": true + } +} +``` + diff --git a/Api.Documentation/icon.png b/Api.Documentation/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.ErrorHandling/.docker/docker-compose.yml b/Api.ErrorHandling/.docker/docker-compose.yml new file mode 100644 index 00000000..7a9a44dc --- /dev/null +++ b/Api.ErrorHandling/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.errorhandling: + image: api.errorhandling + hostname: api-errorhandling + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.ErrorHandling + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge \ No newline at end of file diff --git a/Api.ErrorHandling/.dockerignore b/Api.ErrorHandling/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.ErrorHandling/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.ErrorHandling/.github/config/slack.yml b/Api.ErrorHandling/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.ErrorHandling/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ErrorHandling/.github/workflows/build-and-deploy.yml b/Api.ErrorHandling/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..012b1be2 --- /dev/null +++ b/Api.ErrorHandling/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.ErrorHandling + IMAGE_NAME: api.errorhandling + SERVICE_NAME: api-errorhandling + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ErrorHandling/.gitignore b/Api.ErrorHandling/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.ErrorHandling/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ErrorHandling/.kubernetes/autoscaler.yaml b/Api.ErrorHandling/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.ErrorHandling/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ErrorHandling/.kubernetes/configmap.yaml b/Api.ErrorHandling/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.ErrorHandling/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ErrorHandling/.kubernetes/deployment.yaml b/Api.ErrorHandling/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.ErrorHandling/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.ErrorHandling/.kubernetes/service.yaml b/Api.ErrorHandling/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.ErrorHandling/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Properties/DoNotParallelize.cs b/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Tests.Api.ErrorHandling.csproj b/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Tests.Api.ErrorHandling.csproj new file mode 100644 index 00000000..83f4b412 --- /dev/null +++ b/Api.ErrorHandling/.tests/Tests.Api.ErrorHandling/Tests.Api.ErrorHandling.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj b/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj new file mode 100644 index 00000000..890be2f1 --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling.Models/Api.ErrorHandling.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling.sln b/Api.ErrorHandling/Api.ErrorHandling.sln new file mode 100644 index 00000000..b6538ddd --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ErrorHandling.Models", "Api.ErrorHandling.Models\Api.ErrorHandling.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ErrorHandling", "Api.ErrorHandling\Api.ErrorHandling.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ErrorHandling", ".tests\Tests.Api.ErrorHandling\Tests.Api.ErrorHandling.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.ErrorHandling/Api.ErrorHandling/Api.ErrorHandling.csproj b/Api.ErrorHandling/Api.ErrorHandling/Api.ErrorHandling.csproj new file mode 100644 index 00000000..3bf684a5 --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/Api.ErrorHandling.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/Controllers/ExamplesController.cs b/Api.ErrorHandling/Api.ErrorHandling/Controllers/ExamplesController.cs new file mode 100644 index 00000000..16fd1e6b --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/Controllers/ExamplesController.cs @@ -0,0 +1,235 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Exceptions; +using Nano.Data.Abstractions.Exceptions; +using Vivet.AspNetCore.RequestVirusScan.Exceptions; +using Vivet.AspNetCore.RequestVirusScan.Models.Enums; + +namespace Api.ErrorHandling.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Error. + [HttpGet] + [Route("exception")] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task ExceptionAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new Exception("error"); + } + + /// + /// Aggregate Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Request Timeout. + [HttpGet] + [Route("exception-aggregate")] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task AggregateAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new AggregateException("error-aggregate", new Exception("inner exception")); + } + + /// + /// Virus Scan Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Bad Request. + [HttpGet] + [Route("exception-virus-scan")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public virtual async Task VirusScanAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new VirusScanException(ResultType.VirusFound, "file.exe", "Dangerous Virus"); + } + + /// + /// Unauthorized Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Unauthorized. + [HttpGet] + [Route("exception-unauthorized")] + [ProducesResponseType((int)HttpStatusCode.Unauthorized)] + public virtual async Task UnauthorizedAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new UnauthorizedException(); + } + + /// + /// Permission Denied Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Forbidden. + [HttpGet] + [Route("exception-permission-denied")] + [ProducesResponseType((int)HttpStatusCode.Forbidden)] + public virtual async Task PermissionDeniedAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new PermissionDeniedException(); + } + + /// + /// Identity Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Bad Request. + [HttpGet] + [Route("exception-identity")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public virtual async Task IdentityAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new IdentityException("error"); + } + + /// + /// Bad Request Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Bad Request. + [HttpGet] + [Route("exception-bad-request")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public virtual async Task BadRequestAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new BadRequestException("error"); + } + + /// + /// Bad Request (Coded) Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Bad Request. + [HttpGet] + [Route("exception-bad-request-coded")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public virtual async Task BadRequestCodedAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new BadRequestException("error-coded", true); + } + + /// + /// Bad Request (Translated) Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Bad Request. + [HttpGet] + [Route("exception-bad-request-translated")] + [ProducesResponseType((int)HttpStatusCode.BadRequest)] + public virtual async Task BadRequestTranslatedAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new BadRequestException("error-translated", false, true); + } + + /// + /// Task Canceled Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Request Timeout. + [HttpGet] + [Route("exception-task-canceled")] + [ProducesResponseType((int)HttpStatusCode.RequestTimeout)] + public virtual async Task TaskCanceledAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new TaskCanceledException("error-task-cancelled"); + } + + /// + /// Operation Cancelled Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Request Timeout. + [HttpGet] + [Route("exception-operation-canceled")] + [ProducesResponseType((int)HttpStatusCode.RequestTimeout)] + public virtual async Task OperationCancelledAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new OperationCanceledException("error-operation-cancelled"); + } + + /// + /// Problem Details Exception Action. + /// + /// The cancellation token. + /// Nothing. + /// Loop Detected. + [HttpGet] + [Route("exception-problem-details")] + [ProducesResponseType((int)HttpStatusCode.LoopDetected)] + public virtual async Task ProblemDetailsAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new ProblemDetailsException(new ProblemDetails + { + Status = (int)HttpStatusCode.LoopDetected, + Title = "Loop Detected", + Detail = "Error" + }); + } + + /// + /// Bad Request Validation Action. + /// + /// + /// The cancellation token. + /// A message. + /// Error. + [HttpGet] + [Route("validation-error")] + [ProducesResponseType((int)HttpStatusCode.InternalServerError)] + public virtual async Task BadRequestValidationAsync([FromQuery][Required]string value, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok(); + } +} \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local b/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local new file mode 100644 index 00000000..77abb0b3 --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ErrorHandling.dll"] \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/Program.cs b/Api.ErrorHandling/Api.ErrorHandling/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ErrorHandling/Api.ErrorHandling/Properties/InternalsVisibleTo.cs b/Api.ErrorHandling/Api.ErrorHandling/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..cd41041c --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ErrorHandling")] \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Development.json b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Production.json b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/appsettings.Staging.json b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ErrorHandling/Api.ErrorHandling/appsettings.json b/Api.ErrorHandling/Api.ErrorHandling/appsettings.json new file mode 100644 index 00000000..5c5be510 --- /dev/null +++ b/Api.ErrorHandling/Api.ErrorHandling/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "ErrorHandling": { + "ExposeErrors": true + } + } +} \ No newline at end of file diff --git a/Api.ErrorHandling/Dockerfile b/Api.ErrorHandling/Dockerfile new file mode 100644 index 00000000..de88ee4d --- /dev/null +++ b/Api.ErrorHandling/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.ErrorHandling.dll"] \ No newline at end of file diff --git a/Api.ErrorHandling/LICENSE b/Api.ErrorHandling/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.ErrorHandling/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.ErrorHandling/README.md b/Api.ErrorHandling/README.md new file mode 100644 index 00000000..cec2c4c9 --- /dev/null +++ b/Api.ErrorHandling/README.md @@ -0,0 +1,54 @@ +# Api.ErrorHandling + +> _Nano API application showing error handling._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example demonstrates how Nano API error handling processes exceptions and other errors to produce the appropriate HTTP responses. + +The following endpoint is available for testing: + +| Endpoint | Description | +| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/exception` | Returns a `500 Internal Server Error` response. `Detail` exposed because `ExposeErrors` is set to `true`. | +| `http://localhost:8080/api/examples/exception-aggregate` | Returns a `500 Internal Server Error` response. `Detail` exposed because `ExposeErrors` is set to `true`. | +| `http://localhost:8080/api/examples/exception-virus-scan` | Returns a `400 Bad Request` response. `Detail` always exposed. | +| `http://localhost:8080/api/examples/exception-unauthorized` | Returns a `401 Unauthorized` response. `Detail` always exposed. | +| `http://localhost:8080/api/examples/exception-permission-denied` | Returns a `403 Forbidden` response. Usually no `Detail`, but always exposed. | +| `http://localhost:8080/api/examples/exception-identity` | Returns a `400 Bad Request` response. `Detail` always exposed. | +| `http://localhost:8080/api/examples/exception-bad-request` | Returns a `400 Bad Request` response. `Detail` always exposed. | +| `http://localhost:8080/api/examples/exception-bad-request-coded` | Returns a `400 Bad Request` response. `Detail` always exposed. `IsCoded` property added and set to `true`. | +| `http://localhost:8080/api/examples/exception-bad-request-translated` | Returns a `400 Bad Request` response. `Detail` always exposed. `IsTranslated` property added set to `true`. | +| `http://localhost:8080/api/examples/exception-task-canceled` | Returns a `408 Request Timeout` response. `Detail` always exposed. | +| `http://localhost:8080/api/examples/exception-operation-canceled` | Returns a `408 Request Timeout` response. `Detail` always exposed. | +| `http://localhost:8080/api/examples/exception-problem-details` | Varies depending on the `ProblemDetails`. | +| `http://localhost:8080/api/examples/validation-error` | Returns a `400 Bad Request` response. `Detail` always exposed. | + +Alternatively, toggle the `ExposeErrors` to `false`, and observe that messages from `500 Internal Server Errors` no longer will be exposed. + +> 📖 Learn more about **[Nano Error Handling](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#error-handling)**. + +## Configuration +```json +"App": { + "ErrorHandling": { + "ExposeErrors": true + } +} +``` diff --git a/Api.ErrorHandling/icon.png b/Api.ErrorHandling/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/.docker/docker-compose.yml b/Api.Eventing.RabbitMq/.docker/docker-compose.yml new file mode 100644 index 00000000..de9772ea --- /dev/null +++ b/Api.Eventing.RabbitMq/.docker/docker-compose.yml @@ -0,0 +1,34 @@ +services: + api.eventing.rabbitmq: + image: api.eventing.rabbitmq + hostname: api-eventing-rabbitmq + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Eventing.RabbitMq + dockerfile: "Dockerfile.Local" + depends_on: + - eventing + networks: + - network + + eventing: + image: rabbitmq:management + hostname: rabbitmq + ports: + - 5671:5671 + - 5672:5672 + - 15671:15671 + - 15672:15672 + networks: + - network + environment: + RABBITMQ_DEFAULT_USER: rabbitmq_user + RABBITMQ_DEFAULT_PASS: password + RABBITMQ_DEFAULT_VHOST: / + +networks: + network: + name: network + driver: bridge diff --git a/Api.Eventing.RabbitMq/.dockerignore b/Api.Eventing.RabbitMq/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Eventing.RabbitMq/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Eventing.RabbitMq/.github/config/slack.yml b/Api.Eventing.RabbitMq/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Eventing.RabbitMq/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml b/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..e4a92c54 --- /dev/null +++ b/Api.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Eventing.RabbitMq + IMAGE_NAME: api.eventing.rabbitmq + SERVICE_NAME: api.eventing-rabbitmq + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/.gitignore b/Api.Eventing.RabbitMq/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Eventing.RabbitMq/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/.kubernetes/autoscaler.yaml b/Api.Eventing.RabbitMq/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Eventing.RabbitMq/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml b/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Eventing.RabbitMq/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml b/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml new file mode 100644 index 00000000..c8e22634 --- /dev/null +++ b/Api.Eventing.RabbitMq/.kubernetes/deployment.yaml @@ -0,0 +1,90 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + env: + - name: Eventing__Credentials__Id + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: username + envFrom: + - name: Eventing__Credentials__Secret + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: password + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Eventing.RabbitMq/.kubernetes/service.yaml b/Api.Eventing.RabbitMq/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Eventing.RabbitMq/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Properties/DoNotParallelize.cs b/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Tests.Api.Eventing.RabbitMq.csproj b/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Tests.Api.Eventing.RabbitMq.csproj new file mode 100644 index 00000000..6bca9ffb --- /dev/null +++ b/Api.Eventing.RabbitMq/.tests/Tests.Api.Eventing.RabbitMq/Tests.Api.Eventing.RabbitMq.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj new file mode 100644 index 00000000..04328227 --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.Models/Api.Eventing.RabbitMq.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.sln b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.sln new file mode 100644 index 00000000..60830083 --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.sln @@ -0,0 +1,147 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Eventing.RabbitMq.Models", "Api.Eventing.RabbitMq.Models\Api.Eventing.RabbitMq.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Eventing.RabbitMq", "Api.Eventing.RabbitMq\Api.Eventing.RabbitMq.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Eventing.RabbitMq", ".tests\Tests.Api.Eventing.RabbitMq\Tests.Api.Eventing.RabbitMq.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing", "..\..\Nano.Library\Nano.Eventing\Nano.Eventing.csproj", "{A8E623BC-70EA-3CC8-AFAD-797F006C4A41}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.RabbitMq", "..\..\Nano.Library\Nano.Eventing.RabbitMq\Nano.Eventing.RabbitMq.csproj", "{0789C863-B371-F968-B9C9-5F3CF3DD9897}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.Build.0 = Release|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {0789C863-B371-F968-B9C9-5F3CF3DD9897} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.csproj b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.csproj new file mode 100644 index 00000000..a4d4f7dd --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Controllers/ExamplesController.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Controllers/ExamplesController.cs new file mode 100644 index 00000000..cfa5a821 --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Controllers/ExamplesController.cs @@ -0,0 +1,58 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.Eventing.RabbitMq.Eventing.Models; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Eventing.Abstractions; + +namespace Api.Eventing.RabbitMq.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IEventing eventing) : BaseController(logger) +{ + /// + /// Eventing Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("eventing")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task EventingAsync(CancellationToken cancellationToken = default) + { + await eventing + .PublishAsync(new EventModel + { + Text = "Testing eventing" + }, cancellationToken: cancellationToken); + + return this.Ok("eventing"); + } + + /// + /// Eventing with Routing Key Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("eventing-routing-key")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task EventingRoutingKeyAsync(CancellationToken cancellationToken = default) + { + await eventing + .PublishAsync(new EventModelRoutingKey + { + Text = "Testing eventing" + }, "routing-key", cancellationToken); + + return this.Ok("eventing-routing-key"); + } +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local new file mode 100644 index 00000000..e183367d --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandler.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandler.cs new file mode 100644 index 00000000..3d34f4e5 --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandler.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Api.Eventing.RabbitMq.Eventing.Models; +using Nano.Eventing.Abstractions; + +namespace Api.Eventing.RabbitMq.Eventing; + +/// +public class EventingHandler : BaseEventHandler +{ + /// + public override async Task CallbackAsync(EventModel @event, bool isRedelivered, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + Console.WriteLine(@event.Text); + } +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandlerRoutingKey.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandlerRoutingKey.cs new file mode 100644 index 00000000..b9b49f5b --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/EventingHandlerRoutingKey.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Api.Eventing.RabbitMq.Eventing.Models; +using Nano.Eventing.Abstractions; + +namespace Api.Eventing.RabbitMq.Eventing; + +/// +public class EventingHandlerRoutingKey : BaseEventHandler +{ + /// + /// Routing Key. + /// + public static string RoutingKey => "routing-key"; + + /// + public override async Task CallbackAsync(EventModelRoutingKey @event, bool isRedelivered, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + Console.WriteLine($"Event Routed: {@event.Text}"); + } +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModel.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModel.cs new file mode 100644 index 00000000..6974e555 --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModel.cs @@ -0,0 +1,12 @@ +namespace Api.Eventing.RabbitMq.Eventing.Models; + +/// +/// Event Model. +/// +public class EventModel +{ + /// + /// Text. + /// + public virtual string? Text { get; set; } +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModelRoutingKey.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModelRoutingKey.cs new file mode 100644 index 00000000..8c688a0b --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Eventing/Models/EventModelRoutingKey.cs @@ -0,0 +1,12 @@ +namespace Api.Eventing.RabbitMq.Eventing.Models; + +/// +/// Event Model Routing Key. +/// +public class EventModelRoutingKey +{ + /// + /// Text. + /// + public virtual string? Text { get; set; } +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Program.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Program.cs new file mode 100644 index 00000000..a2213efa --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Api; +using Nano.Eventing.Extensions; +using Nano.Eventing.RabbitMq; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoEventing(); + }) + .Build() + .Run(); diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..b9420e8f --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Eventing.RabbitMq")] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Development.json b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Development.json new file mode 100644 index 00000000..b1f4b0f1 --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Eventing": { + "Credentials": { + "Id": "rabbitmq_user", + "Secret": "password" + } + } +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Production.json b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Staging.json b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.json b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.json new file mode 100644 index 00000000..fd937648 --- /dev/null +++ b/Api.Eventing.RabbitMq/Api.Eventing.RabbitMq/appsettings.json @@ -0,0 +1,31 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + } + }, + "Eventing": { + "Host": "rabbitmq", + "VHost": "/", + "Port": 5672, + "UseSsl": false, + "Timeout": "00:00:30", + "Heartbeat": 60, + "PrefetchCount": 50, + "Credentials": { + "Id": null, + "Secret": null + }, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } +} \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/Dockerfile b/Api.Eventing.RabbitMq/Dockerfile new file mode 100644 index 00000000..4775d14f --- /dev/null +++ b/Api.Eventing.RabbitMq/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/LICENSE b/Api.Eventing.RabbitMq/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Eventing.RabbitMq/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Eventing.RabbitMq/README.md b/Api.Eventing.RabbitMq/README.md new file mode 100644 index 00000000..853d3e09 --- /dev/null +++ b/Api.Eventing.RabbitMq/README.md @@ -0,0 +1,151 @@ +# Api.Eventing.RabbitMq + +> _Nano API application with rabbitmq eventing._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +While it is uncommon for the same application to both publish and consume an event, this pattern is used here for demonstration purposes. +In real-world scenarios, one service typically publishes events while other services consume them. For simplicity, this example combines both roles in a single service. + +Run the endpoints and observe an `EventModel` instance being published and handled in the `EventingHandler`. Check the console output for +the message: `Testing eventing` or `Event Routed: Testing eventing`, depending on the endpoint. This message is written by the `EventingHandler` when the +event is received. + +You can also monitor the messages via the RabbitMQ management interface: **[http://localhost:15672](http://localhost:15672)** + +An eventing health check is configured to target the RabbitMQ. +Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#health-checks)** + +The following endpoint is available for testing: + +| Endpoint | Description | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/eventing` | Returns a simple `200 OK` response. Publishes a message that wiil be consumed by the `EventHandler` | +| `http://localhost:8080/api/examples/eventing-routing-key` | Returns a simple `200 OK` response. Publishes a message using routing key that wiil be consumed by the `EventHandler` | + +> 📖 Learn more about **[Nano.Eventing.RabbitMq](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Eventing.RabbitMq/README.md#nanoeventingrabbitmq)**. + +## Registration +The following eventing provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoEventing(); +}) +... +``` + +## Configuration +Configured the application `appsettings.json` with the necessary eventing setup. + +```json +"Eventing": { + "Host": "rabbitmq", + "VHost": "/", + "Port": 5672, + "UseSsl": false, + "Timeout": "00:00:30", + "Heartbeat": 60, + "PrefetchCount": 50, + "Credentials": { + "Id": "rabbitmq_user", + "Secret": "password" + }, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } +} +``` + +...and the `appsettings.Development.json` eventing configuration. + +```json +"Eventing": { + "Credentials": { + "Id": "rabbitmq_user", + "Secret": "password" + } +} +``` + +Additionally, application health-checks have been enabled with the configuration. + +```json +"App": { + "HealthCheck": { + } +} +``` + +## Docker Compose +Added RabbitMQ as a service dependency in `docker-compose.yml`. + +```yaml +services: + api.eventing.rabbitmq: + depends_on: + - eventing + + eventing: + image: rabbitmq:management + hostname: rabbitmq + ports: + - 5671:5671 + - 5672:5672 + - 15671:15671 + - 15672:15672 + networks: + - network + environment: + RABBITMQ_DEFAULT_USER: rabbitmq_user + RABBITMQ_DEFAULT_PASS: password + RABBITMQ_DEFAULT_VHOST: / + +``` +## Kubernetes +Added the `rabbitmq` secret for password to the `deployment.yaml`. + +```json +spec: + template: + spec: + containers: + env: + - name: Eventing__Credentials__Id + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: username + envFrom: + - name: Eventing__Credentials__Secret + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: password +``` + +> ⚠️ The `rabbitmq` secret is created alongside the **[Nano Azure Kubernetes Eventing](https://github.com/Nano-Core/Nano.Azure.Kubernetes/tree/master/Nano.Azure.Kubernetes.RabbitMQ)** diff --git a/Api.Eventing.RabbitMq/icon.png b/Api.Eventing.RabbitMq/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.HealthChecks/.docker/docker-compose.yml b/Api.HealthChecks/.docker/docker-compose.yml new file mode 100644 index 00000000..dc34bde1 --- /dev/null +++ b/Api.HealthChecks/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.healthchecks: + image: api.healthchecks + hostname: api-healthchecks + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.HealthChecks + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.HealthChecks/.dockerignore b/Api.HealthChecks/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.HealthChecks/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.HealthChecks/.github/config/slack.yml b/Api.HealthChecks/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.HealthChecks/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.HealthChecks/.github/workflows/build-and-deploy.yml b/Api.HealthChecks/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..e09d6b24 --- /dev/null +++ b/Api.HealthChecks/.github/workflows/build-and-deploy.yml @@ -0,0 +1,244 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.HealthChecks + IMAGE_NAME: api.healthchecks + SERVICE_NAME: api-healthchecks + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + AVAILABILITY_URI: ${{ github.ref == 'refs/heads/master' && format('https://{0}/healthz', secrets.PRODUCTION_HOST) || format('https://{0}/healthz', secrets.STAGING_HOST) }} + AVAILABILITY_CHECK_FREQUENCY: 300 +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Add Availability Check + id: add-availability-check + shell: pwsh + run: | + az extension add -n application-insights; + + $env:SERVICE_NAME_INSIGTHS = $env:SERVICE_NAME + "-insights"; + $env:APPLICATION_INSIGHT_ID = az monitor app-insights component show --query "[?contains(name, '$env:SERVICE_NAME_INSIGTHS')].[id]" -o tsv; + + if ([string]::IsNullOrEmpty($env:APPLICATION_INSIGHT_ID)) + { + $env:WORKSPACE_ID = az monitor log-analytics workspace list --query "[?contains(name, 'log-analytics')].[id]" -o tsv; + + if (-not [string]::IsNullOrEmpty($env:WORKSPACE_ID)) + { + $env:APPLICATION_INSIGHT_ID = az monitor app-insights component create ` + -a $env:SERVICE_NAME_INSIGTHS ` + -l $env:AZURE_LOCATION ` + -g $env:AZURE_GROUP ` + --workspace $env:WORKSPACE_ID ` + --query "[id]" -o tsv; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + }; + + $env:SERVICE_NAME_AVAILABILITY = $env:SERVICE_NAME + '-availability-' + $env:ASPNETCORE_ENVIRONMENT.ToLower(); + $env:AVAILABILITY_ID = az monitor app-insights web-test list -g $env:AZURE_GROUP --query "[?contains(name, '$env:SERVICE_NAME_AVAILABILITY')].[id]" -o tsv; + + if ([string]::IsNullOrEmpty($env:AVAILABILITY_ID)) + { + $env:APPLICATION_INSIGHT_HIDDEN_LINK = 'hidden-link:' + $env:APPLICATION_INSIGHT_ID + '=Resource'; + az monitor app-insights web-test create ` + -n $env:SERVICE_NAME_AVAILABILITY ` + --defined-web-test-name $env:SERVICE_NAME_AVAILABILITY ` + -g $env:AZURE_GROUP ` + -l $env:AZURE_LOCATION ` + --kind multistep ` + --web-test-kind standard ` + --frequency $env:AVAILABILITY_CHECK_FREQUENCY ` + --enabled true ` + --retry-enabled true ` + --ssl-check true ` + --ssl-lifetime-check 30 ` + --http-verb GET ` + --request-url $env:AVAILABILITY_URI ` + --expected-status-code 200 ` + --content-validation content-match='\"status\":\"unhealthy\"' ignore-case=true pass-if-text-found=false ` + --tags $env:APPLICATION_INSIGHT_HIDDEN_LINK ` + --locations Id='us-ca-sjc-azr' ` + --locations Id='us-va-ash-azr' ` + --locations Id='emea-gb-db3-azr' ` + --locations Id='emea-nl-ams-azr' ` + --locations Id='apac-hk-hkn-azr'; + }; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.HealthChecks/.gitignore b/Api.HealthChecks/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.HealthChecks/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.HealthChecks/.kubernetes/autoscaler.yaml b/Api.HealthChecks/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.HealthChecks/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.HealthChecks/.kubernetes/configmap.yaml b/Api.HealthChecks/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.HealthChecks/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.HealthChecks/.kubernetes/deployment.yaml b/Api.HealthChecks/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.HealthChecks/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.HealthChecks/.kubernetes/service.yaml b/Api.HealthChecks/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.HealthChecks/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Properties/DoNotParallelize.cs b/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Tests.Api.HealthChecks.csproj b/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Tests.Api.HealthChecks.csproj new file mode 100644 index 00000000..5b02dede --- /dev/null +++ b/Api.HealthChecks/.tests/Tests.Api.HealthChecks/Tests.Api.HealthChecks.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj b/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks.Models/Api.HealthChecks.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks.sln b/Api.HealthChecks/Api.HealthChecks.sln new file mode 100644 index 00000000..50d5122c --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.HealthChecks.Models", "Api.HealthChecks.Models\Api.HealthChecks.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.HealthChecks", "Api.HealthChecks\Api.HealthChecks.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.HealthChecks", ".tests\Tests.Api.HealthChecks\Tests.Api.HealthChecks.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.HealthChecks/Api.HealthChecks/Api.HealthChecks.csproj b/Api.HealthChecks/Api.HealthChecks/Api.HealthChecks.csproj new file mode 100644 index 00000000..83ddff6a --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/Api.HealthChecks.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/Controllers/ExamplesController.cs b/Api.HealthChecks/Api.HealthChecks/Controllers/ExamplesController.cs new file mode 100644 index 00000000..7763d523 --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/Controllers/ExamplesController.cs @@ -0,0 +1,54 @@ +using System; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Diagnostics.HealthChecks; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.HealthChecks.Controllers; + +/// +/// Controller with examples. +/// +/// The +/// The . +public class ExamplesController(ILogger logger, HealthCheckService healthCheckService) + : BaseController(logger) +{ + private readonly HealthCheckService healthCheckService = healthCheckService ?? throw new ArgumentNullException(nameof(healthCheckService)); + + /// + /// Health Check Action. + /// + /// The cancellation token. + /// A message. + /// Success. + /// Service Unavailable. + [HttpGet] + [Route("health-check")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task HealthCheckAsync(CancellationToken cancellationToken = default) + { + var report = await this.healthCheckService + .CheckHealthAsync(cancellationToken); + + var status = report.Status == HealthStatus.Healthy + ? (int)HttpStatusCode.OK + : (int)HttpStatusCode.ServiceUnavailable; + + return this.StatusCode(status, new + { + status = report.Status.ToString(), + checks = report.Entries + .Select(x => new + { + name = x.Key, + status = x.Value.Status.ToString(), + description = x.Value.Description + }) + }); + } +} \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local b/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local new file mode 100644 index 00000000..927b61e9 --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.HealthChecks.dll"] \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/Program.cs b/Api.HealthChecks/Api.HealthChecks/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.HealthChecks/Api.HealthChecks/Properties/InternalsVisibleTo.cs b/Api.HealthChecks/Api.HealthChecks/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..a92131f4 --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.HealthChecks")] \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json b/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.Production.json b/Api.HealthChecks/Api.HealthChecks/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.Staging.json b/Api.HealthChecks/Api.HealthChecks/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.HealthChecks/Api.HealthChecks/appsettings.json b/Api.HealthChecks/Api.HealthChecks/appsettings.json new file mode 100644 index 00000000..053b76d1 --- /dev/null +++ b/Api.HealthChecks/Api.HealthChecks/appsettings.json @@ -0,0 +1,15 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + } + } +} \ No newline at end of file diff --git a/Api.HealthChecks/Dockerfile b/Api.HealthChecks/Dockerfile new file mode 100644 index 00000000..2e170ee4 --- /dev/null +++ b/Api.HealthChecks/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.HealthChecks.dll"] \ No newline at end of file diff --git a/Api.HealthChecks/LICENSE b/Api.HealthChecks/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.HealthChecks/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.HealthChecks/README.md b/Api.HealthChecks/README.md new file mode 100644 index 00000000..18536976 --- /dev/null +++ b/Api.HealthChecks/README.md @@ -0,0 +1,124 @@ +# Api.HealthChecks + +> _Nano API application with health checks._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) +* [GitHub Actions](#gitHub-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example illustrates the use of Nano API health-checks. + +Open [http://localhost:8080/healthz](http://localhost:8080/healthz) to view the startup health-check JSON report. + +The following endpoints are available for testing. + +| Endpoint | Description | +| ------------------------------------------------- | ------------------------------------------------------ | +| `http://localhost:8080/api/examples/health-check` | Returns a `200 OK` response with health-check status. | + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Apihealth-checks)**. + +## Configuration +There is no configuration for HealtCheck, the section has just been added to enable the feature. + +```json +"App": { + "HealthCheck": { + } +} +``` + +## GitHub Actions +Optionally, you can configure an availability check in Azure using Application Insights to continuously monitor your application's health and responsiveness. + +> ⚠️ This requires the application to be exposed publicly from Kubernetes via an `ingress` configuration. + +Add the following environment variables. + +```yaml +env: + AVAILABILITY_URI: ${{ github.ref == 'refs/heads/master' && format('https://{0}/healthz', secrets.PRODUCTION_HOST) || format('https://{0}/healthz', secrets.STAGING_HOST) }} + AVAILABILITY_CHECK_FREQUENCY: 300 +``` + +...and then add the `Add Availability Check` step to the action pipeline. + +```yaml + - name: Add Availability Check + id: add-availability-check + shell: pwsh + run: | + sudo az extension add -n application-insights; + + $env:SERVICE_NAME_INSIGTHS = $env:SERVICE_NAME + "-insights"; + $env:APPLICATION_INSIGHT_ID = sudo az monitor app-insights component show --query "[?contains(name, '$env:SERVICE_NAME_INSIGTHS')].[id]" -o tsv; + + if ([string]::IsNullOrEmpty($env:APPLICATION_INSIGHT_ID)) + { + $env:WORKSPACE_ID = sudo az monitor log-analytics workspace list --query "[?contains(name, 'log-analytics')].[id]" -o tsv; + + if (-not [string]::IsNullOrEmpty($env:WORKSPACE_ID)) + { + $env:APPLICATION_INSIGHT_ID = sudo az monitor app-insights component create ` + -a $env:SERVICE_NAME_INSIGTHS ` + -l $env:AZURE_LOCATION ` + -g $env:AZURE_GROUP ` + --workspace $env:WORKSPACE_ID ` + --query "[id]" -o tsv; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + }; + + $env:SERVICE_NAME_AVAILABILITY = $env:SERVICE_NAME + '-availability-' + $env:ASPNETCORE_ENVIRONMENT.ToLower(); + $env:AVAILABILITY_ID = sudo az monitor app-insights web-test list -g $env:AZURE_GROUP --query "[?contains(name, '$env:SERVICE_NAME_AVAILABILITY')].[id]" -o tsv; + + if ([string]::IsNullOrEmpty($env:AVAILABILITY_ID)) + { + $env:APPLICATION_INSIGHT_HIDDEN_LINK = 'hidden-link:' + $env:APPLICATION_INSIGHT_ID + '=Resource'; + sudo az monitor app-insights web-test create ` + -n $env:SERVICE_NAME_AVAILABILITY ` + --defined-web-test-name $env:SERVICE_NAME_AVAILABILITY ` + -g $env:AZURE_GROUP ` + -l $env:AZURE_LOCATION ` + --kind multistep ` + --web-test-kind standard ` + --frequency $env:AVAILABILITY_CHECK_FREQUENCY ` + --enabled true ` + --retry-enabled true ` + --ssl-check true ` + --ssl-lifetime-check 30 ` + --http-verb GET ` + --request-url $env:AVAILABILITY_URI ` + --expected-status-code 200 ` + --content-validation content-match='\"status\":\"unhealthy\"' ignore-case=true pass-if-text-found=false ` + --tags $env:APPLICATION_INSIGHT_HIDDEN_LINK ` + --locations Id='us-ca-sjc-azr' ` + --locations Id='us-va-ash-azr' ` + --locations Id='emea-gb-db3-azr' ` + --locations Id='emea-nl-ams-azr' ` + --locations Id='apac-hk-hkn-azr'; + }; + if ($LastExitCode -ne 0) + { + throw "error"; + }; +``` diff --git a/Api.HealthChecks/icon.png b/Api.HealthChecks/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Hosting.Http/.docker/docker-compose.yml b/Api.Hosting.Http/.docker/docker-compose.yml new file mode 100644 index 00000000..04d84d49 --- /dev/null +++ b/Api.Hosting.Http/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.hosting.http: + image: api.hosting.http + hostname: api-hosting-http + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Hosting.Http + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Hosting.Http/.dockerignore b/Api.Hosting.Http/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Hosting.Http/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Hosting.Http/.github/config/slack.yml b/Api.Hosting.Http/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Hosting.Http/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Hosting.Http/.github/workflows/build-and-deploy.yml b/Api.Hosting.Http/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..730be7cd --- /dev/null +++ b/Api.Hosting.Http/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Hosting.Http + IMAGE_NAME: api.hosting.http + SERVICE_NAME: api-hosting-http + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Hosting.Http/.gitignore b/Api.Hosting.Http/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Hosting.Http/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Hosting.Http/.kubernetes/autoscaler.yaml b/Api.Hosting.Http/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Hosting.Http/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Hosting.Http/.kubernetes/configmap.yaml b/Api.Hosting.Http/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Hosting.Http/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Hosting.Http/.kubernetes/deployment.yaml b/Api.Hosting.Http/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Hosting.Http/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Hosting.Http/.kubernetes/service.yaml b/Api.Hosting.Http/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Hosting.Http/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Properties/DoNotParallelize.cs b/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Tests.Api.Hosting.Http.csproj b/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Tests.Api.Hosting.Http.csproj new file mode 100644 index 00000000..1777aecb --- /dev/null +++ b/Api.Hosting.Http/.tests/Tests.Api.Hosting.Http/Tests.Api.Hosting.Http.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj b/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http.Models/Api.Hosting.Http.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http.sln b/Api.Hosting.Http/Api.Hosting.Http.sln new file mode 100644 index 00000000..a595b1ed --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.Http.Models", "Api.Hosting.Http.Models\Api.Hosting.Http.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.Http", "Api.Hosting.Http\Api.Hosting.Http.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Hosting.Http", ".tests\Tests.Api.Hosting.Http\Tests.Api.Hosting.Http.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Hosting.Http/Api.Hosting.Http/Api.Hosting.Http.csproj b/Api.Hosting.Http/Api.Hosting.Http/Api.Hosting.Http.csproj new file mode 100644 index 00000000..cd0c7fed --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/Api.Hosting.Http.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/Controllers/ExamplesController.cs b/Api.Hosting.Http/Api.Hosting.Http/Controllers/ExamplesController.cs new file mode 100644 index 00000000..8bc2c176 --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Mvc; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Hosting.Http.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Http Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("http")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task HttpAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("http"); + } +} \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local b/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local new file mode 100644 index 00000000..272c1ffb --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Hosting.Http.dll"] \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/Program.cs b/Api.Hosting.Http/Api.Hosting.Http/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Hosting.Http/Api.Hosting.Http/Properties/InternalsVisibleTo.cs b/Api.Hosting.Http/Api.Hosting.Http/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..e5d9e205 --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Hosting.Http")] \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Development.json b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Production.json b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/appsettings.Staging.json b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Hosting.Http/Api.Hosting.Http/appsettings.json b/Api.Hosting.Http/Api.Hosting.Http/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.Hosting.Http/Api.Hosting.Http/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.Hosting.Http/Dockerfile b/Api.Hosting.Http/Dockerfile new file mode 100644 index 00000000..2d614ac6 --- /dev/null +++ b/Api.Hosting.Http/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Hosting.Http.dll"] \ No newline at end of file diff --git a/Api.Hosting.Http/LICENSE b/Api.Hosting.Http/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Hosting.Http/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Hosting.Http/README.md b/Api.Hosting.Http/README.md new file mode 100644 index 00000000..13c5fa45 --- /dev/null +++ b/Api.Hosting.Http/README.md @@ -0,0 +1,30 @@ +# Api.Hosting.Http + +> _Nano API application with http._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. The rest of the setup remains largely unchanged. + +This example simply shows configuring a Nano application for HTTP exposure. + +The following endpoint is available for testing: + +| Endpoint | Description | +| ------------------------------------------ | -------------------------------------- | +| `http://localhost:8080/api/examples/http` | Returns a simple `200 OK` response. | + +> 📖 Learn more about **[Nano Hosting Http](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#http)**. diff --git a/Api.Hosting.Http/icon.png b/Api.Hosting.Http/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Hosting.Https/.docker/docker-compose.yml b/Api.Hosting.Https/.docker/docker-compose.yml new file mode 100644 index 00000000..62a4f392 --- /dev/null +++ b/Api.Hosting.Https/.docker/docker-compose.yml @@ -0,0 +1,20 @@ +services: + api.hosting.https: + image: api.hosting.https + hostname: api-hosting-https + restart: on-failure + ports: + - 8080:8080 + - 4443:4443 + build: + context: ../Api.Hosting.Https + dockerfile: "Dockerfile.Local" + volumes: + - ../:/root/.dotnet/https + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Hosting.Https/.dockerignore b/Api.Hosting.Https/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Hosting.Https/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Hosting.Https/.github/config/slack.yml b/Api.Hosting.Https/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Hosting.Https/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Hosting.Https/.github/workflows/build-and-deploy.yml b/Api.Hosting.Https/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..629d9c6f --- /dev/null +++ b/Api.Hosting.Https/.github/workflows/build-and-deploy.yml @@ -0,0 +1,205 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Hosting.Https + IMAGE_NAME: api.hosting.https + SERVICE_NAME: api-hosting-https + SUB_DOMAIN_NAME: nano + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query "[].name" -o json | ConvertFrom-Json + + $env:ROUTE_HOST_NAMES = ( + $zoneNames | ForEach-Object { + " - $env:SUB_DOMAIN_NAME.$_" + } + ) -join "`n" + + $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/grafana-httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-80.tmp.yaml; + kubectl apply -f .kubernetes/grafana-httproute-80.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/grafana-httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-443.tmp.yaml; + kubectl apply -f .kubernetes/grafana-httproute-443.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Hosting.Https/.gitignore b/Api.Hosting.Https/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Hosting.Https/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Hosting.Https/.kubernetes/autoscaler.yaml b/Api.Hosting.Https/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Hosting.Https/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Hosting.Https/.kubernetes/configmap.yaml b/Api.Hosting.Https/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Hosting.Https/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Hosting.Https/.kubernetes/deployment.yaml b/Api.Hosting.Https/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Hosting.Https/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Hosting.Https/.kubernetes/httproute-443.yaml b/Api.Hosting.Https/.kubernetes/httproute-443.yaml new file mode 100644 index 00000000..65b97a51 --- /dev/null +++ b/Api.Hosting.Https/.kubernetes/httproute-443.yaml @@ -0,0 +1,18 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %SERVICE_NAME%-route + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + hostnames: +%ROUTE_HOST_NAMES% + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: %SERVICE_NAME% + port: 8080 \ No newline at end of file diff --git a/Api.Hosting.Https/.kubernetes/httproute-80.yaml b/Api.Hosting.Https/.kubernetes/httproute-80.yaml new file mode 100644 index 00000000..f29775aa --- /dev/null +++ b/Api.Hosting.Https/.kubernetes/httproute-80.yaml @@ -0,0 +1,17 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %APP_NAME%-route-80 + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + sectionName: http + hostnames: +%ROUTE_HOST_NAMES% + rules: + - filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 \ No newline at end of file diff --git a/Api.Hosting.Https/.kubernetes/service.yaml b/Api.Hosting.Https/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Hosting.Https/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Properties/DoNotParallelize.cs b/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Tests.Api.Hosting.Https.csproj b/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Tests.Api.Hosting.Https.csproj new file mode 100644 index 00000000..eff632ef --- /dev/null +++ b/Api.Hosting.Https/.tests/Tests.Api.Hosting.Https/Tests.Api.Hosting.Https.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj b/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj new file mode 100644 index 00000000..7a4d41f3 --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https.Models/Api.Hosting.Https.Models.csproj @@ -0,0 +1,74 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https.sln b/Api.Hosting.Https/Api.Hosting.Https.sln new file mode 100644 index 00000000..6024beda --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https.sln @@ -0,0 +1,136 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + localhost.pfx = localhost.pfx + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\httproute-443.yaml = .kubernetes\httproute-443.yaml + .kubernetes\httproute-80.yaml = .kubernetes\httproute-80.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.Https.Models", "Api.Hosting.Https.Models\Api.Hosting.Https.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.Https", "Api.Hosting.Https\Api.Hosting.Https.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Hosting.Https", ".tests\Tests.Api.Hosting.Https\Tests.Api.Hosting.Https.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Hosting.Https/Api.Hosting.Https/Api.Hosting.Https.csproj b/Api.Hosting.Https/Api.Hosting.Https/Api.Hosting.Https.csproj new file mode 100644 index 00000000..c5f99abc --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/Api.Hosting.Https.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/Controllers/ExamplesController.cs b/Api.Hosting.Https/Api.Hosting.Https/Controllers/ExamplesController.cs new file mode 100644 index 00000000..ab1e443f --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Mvc; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Hosting.Https.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Https Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("https")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task HttpsAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("https"); + } +} \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local b/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local new file mode 100644 index 00000000..e15900e2 --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 4443 + +ENTRYPOINT ["dotnet", "Api.Hosting.Https.dll"] \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/Program.cs b/Api.Hosting.Https/Api.Hosting.Https/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Hosting.Https/Api.Hosting.Https/Properties/InternalsVisibleTo.cs b/Api.Hosting.Https/Api.Hosting.Https/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..34b77663 --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Hosting.Https")] \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json new file mode 100644 index 00000000..23bfb871 --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Development.json @@ -0,0 +1,19 @@ +{ + "App": { + "Hosting": { + "http": { + "UseHttpsRedirection": true + }, + "Https": { + "Ports": [ + 4443 + ], + "Certificate": { + "Path": "/root/.dotnet/https/localhost.pfx", + "Password": "password" + }, + "UseHttpsRequired": true + } + } + } +} \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Production.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.Staging.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Hosting.Https/Api.Hosting.Https/appsettings.json b/Api.Hosting.Https/Api.Hosting.Https/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.Hosting.Https/Api.Hosting.Https/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.Hosting.Https/Dockerfile b/Api.Hosting.Https/Dockerfile new file mode 100644 index 00000000..ed3c969f --- /dev/null +++ b/Api.Hosting.Https/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Hosting.Https.dll"] \ No newline at end of file diff --git a/Api.Hosting.Https/LICENSE b/Api.Hosting.Https/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Hosting.Https/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Hosting.Https/README.md b/Api.Hosting.Https/README.md new file mode 100644 index 00000000..b9bf791d --- /dev/null +++ b/Api.Hosting.Https/README.md @@ -0,0 +1,94 @@ +# Api.Hosting.Https + +> _Nano API application with https._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#gitHub-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example simply shows configuring a Nano application for HTTPS exposure. + +It adds HTTPS configuration, including a `localhost.pfx` self-signed development certificate, and a simple test controller that inherits +from the top-level Nano `BaseController`. + +The following endpoints are available for testing: + +| Endpoint | Description | +| -------------------------------------------- | -------------------------------------- | +| `http://localhost:8080/api/examples/http` | Redirects to HTTPS. | +| `https://localhost:4443/api/examples/https` | Returns a simple `200 OK` response. | + +> 📖 Learn more about **[Nano Hosting HTTPS](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#https)**. + +## Configuration +For `appsettings.json`, nothing has changed - HTTP is still exposed. +HTTPS and the development certificate are only configured in `appsettings.Development.json`. In live environments, HTTPS is handled by the ingress +in Kubernetes. Certificates are managed externally, and the service only needs to expose an HTTP port in `service.yaml` for mapping traffic through the ingress, +which serves HTTPS and forwards it to HTTP. + +```json +"App": { + "Hosting": { + "http": { + "UseHttpsRedirection": true + }, + "Https": { + "Ports": [ + 4443 + ], + "Certificate": { + "Path": "/root/.dotnet/https/localhost.pfx", + "Password": "password" + }, + "UseHttpsRequired": true + } + } +} +``` + +## Docker-compose +Added the following port and certificate path mapping to `docker-compose.yml`. + +```yaml +services: + nano.api.hosting.https: + ports: + - 4443:4443 + volumes: + - ../:/root/.dotnet/https +``` + +## Kubernetes +A `httproute.yaml` resource has been added to the `.kubernetes` folder. + +| File / Directory | Type | Description | +| -------------------- | ------- | -------------------------------------------- | +| `httproute.yaml` | `yaml` | The http route spec for Kubernetes Gateway. | + +## GitHub Actions +Deployment commands have been updated to apply the new Kubernetes `HTTPRoute` template. + +```powershell +Get-Content .kubernetes/{resource-name}.yaml ` + | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` + | Set-Content .kubernetes/{resource-name}.tmp.yaml; + +sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; +``` diff --git a/Api.Hosting.Https/icon.png b/Api.Hosting.Https/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 QtUG7zSmhZ)eg(jP05{mZ`~Uy| literal 0 HcmV?d00001 diff --git a/Api.Hosting.MultipartLimits/.docker/docker-compose.dcproj b/Api.Hosting.MultipartLimits/.docker/docker-compose.dcproj new file mode 100644 index 00000000..d9e8e500 --- /dev/null +++ b/Api.Hosting.MultipartLimits/.docker/docker-compose.dcproj @@ -0,0 +1,13 @@ + + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/.docker/docker-compose.yml b/Api.Hosting.MultipartLimits/.docker/docker-compose.yml new file mode 100644 index 00000000..5fe0aef5 --- /dev/null +++ b/Api.Hosting.MultipartLimits/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.hosting.multipartlimits: + image: api.hosting.multipartlimits + hostname: api-hosting-multipartlimits + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Hosting.MultipartLimits + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Hosting.MultipartLimits/.dockerignore b/Api.Hosting.MultipartLimits/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Hosting.MultipartLimits/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Hosting.MultipartLimits/.github/config/slack.yml b/Api.Hosting.MultipartLimits/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Hosting.MultipartLimits/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml b/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..4ca38002 --- /dev/null +++ b/Api.Hosting.MultipartLimits/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Hosting.MultipartLimits + IMAGE_NAME: api.hosting.multipartlimits + SERVICE_NAME: api-Hosting-multipartlimits + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/.gitignore b/Api.Hosting.MultipartLimits/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Hosting.MultipartLimits/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/.kubernetes/autoscaler.yaml b/Api.Hosting.MultipartLimits/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Hosting.MultipartLimits/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml b/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Hosting.MultipartLimits/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml b/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Hosting.MultipartLimits/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Hosting.MultipartLimits/.kubernetes/service.yaml b/Api.Hosting.MultipartLimits/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Hosting.MultipartLimits/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Properties/DoNotParallelize.cs b/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Tests.Api.Hosting.MultipartLimits.csproj b/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Tests.Api.Hosting.MultipartLimits.csproj new file mode 100644 index 00000000..cf2d7bec --- /dev/null +++ b/Api.Hosting.MultipartLimits/.tests/Tests.Api.Hosting.MultipartLimits/Tests.Api.Hosting.MultipartLimits.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.Models/Api.Hosting.MultipartLimits.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.sln b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.sln new file mode 100644 index 00000000..a6d173be --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.sln @@ -0,0 +1,140 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.MultipartLimits.Models", "Api.Hosting.MultipartLimits.Models\Api.Hosting.MultipartLimits.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Microsoft", "..\..\Nano.Library\Nano.Logging.Microsoft\Nano.Logging.Microsoft.csproj", "{FBFE42A1-83A4-7281-4D54-7F520A06C20F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Hosting.MultipartLimits", "Api.Hosting.MultipartLimits\Api.Hosting.MultipartLimits.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Hosting.MultipartLimits", ".tests\Tests.Api.Hosting.MultipartLimits\Tests.Api.Hosting.MultipartLimits.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {FBFE42A1-83A4-7281-4D54-7F520A06C20F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBFE42A1-83A4-7281-4D54-7F520A06C20F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBFE42A1-83A4-7281-4D54-7F520A06C20F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBFE42A1-83A4-7281-4D54-7F520A06C20F}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {FBFE42A1-83A4-7281-4D54-7F520A06C20F} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.csproj b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.csproj new file mode 100644 index 00000000..9463c71f --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Controllers/ExamplesController.cs b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Controllers/ExamplesController.cs new file mode 100644 index 00000000..1b715a0a --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Controllers/ExamplesController.cs @@ -0,0 +1,37 @@ +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Common.Consts; + +namespace Api.Hosting.MultipartLimits.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Upload File. + /// Max size: 1 MB. + /// + /// The file. + /// The token used when request is cancelled. + /// A message. + /// OK. + [HttpPost] + [Route("upload-file")] + [Consumes(HttpContentType.FORM)] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task UploadFileAsync([Required]IFormFile file, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok($"File: '{file.FileName}' Uploadeed. Size: {file.Length} bytes."); + } +} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local new file mode 100644 index 00000000..e80d58e4 --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Hosting.MultipartLimits.dll"] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Program.cs b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Properties/InternalsVisibleTo.cs b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..cc930f5f --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Hosting.MultipartLimits")] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Development.json b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Production.json b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Staging.json b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.json b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.json new file mode 100644 index 00000000..7d4c0efc --- /dev/null +++ b/Api.Hosting.MultipartLimits/Api.Hosting.MultipartLimits/appsettings.json @@ -0,0 +1,17 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + }, + "MultipartLimits": { + "MaxUploadBytes": 1048576, + "KeepAliveTimeout": 30 + } + } + } +} \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/Dockerfile b/Api.Hosting.MultipartLimits/Dockerfile new file mode 100644 index 00000000..b205db82 --- /dev/null +++ b/Api.Hosting.MultipartLimits/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Hosting.MultipartLimits.dll"] \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/LICENSE b/Api.Hosting.MultipartLimits/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Hosting.MultipartLimits/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Hosting.MultipartLimits/README.md b/Api.Hosting.MultipartLimits/README.md new file mode 100644 index 00000000..b8b04620 --- /dev/null +++ b/Api.Hosting.MultipartLimits/README.md @@ -0,0 +1,47 @@ +# Api.Hosting.MultipartLimits + +> Nano API application with upload limits._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example demonstrates upload limits configured for a Nano application. + +Added multipart limits for file uploads, setting max upload size to 1 MB. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ------------------------------------------------- | -------------------------------------------------------- | +| `http://localhost:8080/api/examples/upload-file` | Upload a file, if larger than 1 MB it will be rejected. | + +> 📖 Learn more about **[Nano MultiPart Limits](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Apimultipart-limits)**. + +## Configuration +Added the following configuration to `appsettings.json`. + +```json +Added + "App": { + "Hosting": { + "MultipartLimits": { + "MaxUploadBytes": 1048576, + "KeepAliveTimeout": 30 + } +} +``` diff --git a/Api.Hosting.MultipartLimits/icon.png b/Api.Hosting.MultipartLimits/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Localization/.docker/docker-compose.yml b/Api.Localization/.docker/docker-compose.yml new file mode 100644 index 00000000..0af53bf1 --- /dev/null +++ b/Api.Localization/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.localization: + image: api.localization + hostname: api-localization + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Localization + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Localization/.dockerignore b/Api.Localization/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Localization/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Localization/.github/config/slack.yml b/Api.Localization/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Localization/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Localization/.github/workflows/build-and-deploy.yml b/Api.Localization/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..5e3244b5 --- /dev/null +++ b/Api.Localization/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Localization + IMAGE_NAME: api.localization + SERVICE_NAME: api-localization + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Localization/.gitignore b/Api.Localization/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Localization/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Localization/.kubernetes/autoscaler.yaml b/Api.Localization/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Localization/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Localization/.kubernetes/configmap.yaml b/Api.Localization/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Localization/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Localization/.kubernetes/deployment.yaml b/Api.Localization/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Localization/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Localization/.kubernetes/service.yaml b/Api.Localization/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Localization/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Localization/.tests/Tests.Api.Localization/Properties/DoNotParallelize.cs b/Api.Localization/.tests/Tests.Api.Localization/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Localization/.tests/Tests.Api.Localization/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Localization/.tests/Tests.Api.Localization/Tests.Api.Localization.csproj b/Api.Localization/.tests/Tests.Api.Localization/Tests.Api.Localization.csproj new file mode 100644 index 00000000..118efd56 --- /dev/null +++ b/Api.Localization/.tests/Tests.Api.Localization/Tests.Api.Localization.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj b/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Localization/Api.Localization.Models/Api.Localization.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Localization/Api.Localization.sln b/Api.Localization/Api.Localization.sln new file mode 100644 index 00000000..49d5760f --- /dev/null +++ b/Api.Localization/Api.Localization.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Localization.Models", "Api.Localization.Models\Api.Localization.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Localization", "Api.Localization\Api.Localization.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Localization", ".tests\Tests.Api.Localization\Tests.Api.Localization.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Localization/Api.Localization/Api.Localization.csproj b/Api.Localization/Api.Localization/Api.Localization.csproj new file mode 100644 index 00000000..e7c0acea --- /dev/null +++ b/Api.Localization/Api.Localization/Api.Localization.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Localization/Api.Localization/Controllers/ExamplesController.cs b/Api.Localization/Api.Localization/Controllers/ExamplesController.cs new file mode 100644 index 00000000..7d96f751 --- /dev/null +++ b/Api.Localization/Api.Localization/Controllers/ExamplesController.cs @@ -0,0 +1,38 @@ +using System.Globalization; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Localization.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Localization Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("localization")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LocalizationAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + + return this.Ok(new + { + CultureInfo.CurrentCulture.Name, + CultureInfo.CurrentCulture.EnglishName, + CultureInfo.CurrentCulture.NativeName + }); + } +} \ No newline at end of file diff --git a/Api.Localization/Api.Localization/Dockerfile.Local b/Api.Localization/Api.Localization/Dockerfile.Local new file mode 100644 index 00000000..4cb9e786 --- /dev/null +++ b/Api.Localization/Api.Localization/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Localization.dll"] \ No newline at end of file diff --git a/Api.Localization/Api.Localization/Program.cs b/Api.Localization/Api.Localization/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Localization/Api.Localization/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Localization/Api.Localization/Properties/InternalsVisibleTo.cs b/Api.Localization/Api.Localization/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..118e4e92 --- /dev/null +++ b/Api.Localization/Api.Localization/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Localization")] \ No newline at end of file diff --git a/Api.Localization/Api.Localization/appsettings.Development.json b/Api.Localization/Api.Localization/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Localization/Api.Localization/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Localization/Api.Localization/appsettings.Production.json b/Api.Localization/Api.Localization/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Localization/Api.Localization/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Localization/Api.Localization/appsettings.Staging.json b/Api.Localization/Api.Localization/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Localization/Api.Localization/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Localization/Api.Localization/appsettings.json b/Api.Localization/Api.Localization/appsettings.json new file mode 100644 index 00000000..218abec2 --- /dev/null +++ b/Api.Localization/Api.Localization/appsettings.json @@ -0,0 +1,19 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Localization": { + "DefaultCulture": "en-US", + "SupportedCultures": [ + "da-DK" + ] + } + } +} \ No newline at end of file diff --git a/Api.Localization/Dockerfile b/Api.Localization/Dockerfile new file mode 100644 index 00000000..14247453 --- /dev/null +++ b/Api.Localization/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Localization.dll"] \ No newline at end of file diff --git a/Api.Localization/LICENSE b/Api.Localization/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Localization/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Localization/README.md b/Api.Localization/README.md new file mode 100644 index 00000000..e3071475 --- /dev/null +++ b/Api.Localization/README.md @@ -0,0 +1,52 @@ +# Api.Localization + +> _Nano API application with request localization._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example shows how to use localized requests in Nano. + +The controller return a response in the following format: + +```json +{ + "Name": "Danish", // The name of the culture language + "EnglishName": "Danish", // The english name of the culture language + "NativeName": "Dansk", // The native name of the culture language +} +``` + +| Endpoint | Description | +| ------------------------------------------------- | ---------------------------------------------------------------- | +| `http://localhost:8080/api/examples/localization` | Returns a `200 OK` response with names of the culture langauge. | + +> 📖 Learn more about **[Nano Localization](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#localization)**. + +## Configuration + +```json +"App": { + "Localization": { + "DefaultCulture": "en-US", + "SupportedCultures": [ + "da-DK" + ] + } +} +``` \ No newline at end of file diff --git a/Api.Localization/icon.png b/Api.Localization/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Logging.Log4Net/.docker/docker-compose.yml b/Api.Logging.Log4Net/.docker/docker-compose.yml new file mode 100644 index 00000000..d0ceb01b --- /dev/null +++ b/Api.Logging.Log4Net/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.logging.log4net: + image: api.logging.log4net + hostname: api-logging-log4net + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Logging.Log4Net + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Logging.Log4Net/.dockerignore b/Api.Logging.Log4Net/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Logging.Log4Net/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Logging.Log4Net/.github/config/slack.yml b/Api.Logging.Log4Net/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Logging.Log4Net/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml b/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..51e37842 --- /dev/null +++ b/Api.Logging.Log4Net/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Logging.Log4Net + IMAGE_NAME: api.logging.log4net + SERVICE_NAME: api-logging-log4net + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Logging.Log4Net/.gitignore b/Api.Logging.Log4Net/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Logging.Log4Net/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Logging.Log4Net/.kubernetes/autoscaler.yaml b/Api.Logging.Log4Net/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Logging.Log4Net/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Logging.Log4Net/.kubernetes/configmap.yaml b/Api.Logging.Log4Net/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Logging.Log4Net/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Logging.Log4Net/.kubernetes/deployment.yaml b/Api.Logging.Log4Net/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Logging.Log4Net/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Logging.Log4Net/.kubernetes/service.yaml b/Api.Logging.Log4Net/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Logging.Log4Net/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Properties/DoNotParallelize.cs b/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Tests.Api.Logging.Log4Net.csproj b/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Tests.Api.Logging.Log4Net.csproj new file mode 100644 index 00000000..247b359b --- /dev/null +++ b/Api.Logging.Log4Net/.tests/Tests.Api.Logging.Log4Net/Tests.Api.Logging.Log4Net.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj b/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj new file mode 100644 index 00000000..6d16235d --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net.Models/Api.Logging.Log4Net.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net.sln b/Api.Logging.Log4Net/Api.Logging.Log4Net.sln new file mode 100644 index 00000000..0dfddf52 --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net.sln @@ -0,0 +1,147 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Log4Net.Models", "Api.Logging.Log4Net.Models\Api.Logging.Log4Net.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Log4Net", "Api.Logging.Log4Net\Api.Logging.Log4Net.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Logging.Log4Net", ".tests\Tests.Api.Logging.Log4Net\Tests.Api.Logging.Log4Net.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Log4Net", "..\..\Nano.Library\Nano.Logging.Log4Net\Nano.Logging.Log4Net.csproj", "{FB775619-7CDE-E029-14F8-7C22944D6DC9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {68215D72-880F-683C-BFEE-ED19A425A5F2} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {FB775619-7CDE-E029-14F8-7C22944D6DC9} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Api.Logging.Log4Net.csproj b/Api.Logging.Log4Net/Api.Logging.Log4Net/Api.Logging.Log4Net.csproj new file mode 100644 index 00000000..256bcf34 --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/Api.Logging.Log4Net.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Controllers/ExamplesController.cs b/Api.Logging.Log4Net/Api.Logging.Log4Net/Controllers/ExamplesController.cs new file mode 100644 index 00000000..86df96a7 --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/Controllers/ExamplesController.cs @@ -0,0 +1,60 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Logging.Log4Net.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Logging Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("logging")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LoggingAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + this.Logger + .LogDebug("Debug"); + + this.Logger + .LogInformation("Information"); + + this.Logger + .LogWarning("Warning"); + + this.Logger + .LogError("Error"); + + return this.Ok("logging"); + } + + /// + /// Logging Exception Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("logging-exception")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LoggingExceptionAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new Exception("test error"); + } +} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local b/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local new file mode 100644 index 00000000..722f4503 --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Program.cs b/Api.Logging.Log4Net/Api.Logging.Log4Net/Program.cs new file mode 100644 index 00000000..5f5829b2 --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Api; +using Nano.Logging.Extensions; +using Nano.Logging.Log4Net; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoLogging(); + }) + .Build() + .Run(); diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/Properties/InternalsVisibleTo.cs b/Api.Logging.Log4Net/Api.Logging.Log4Net/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..057f5be8 --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Logging.Log4Net")] \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Development.json b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Production.json b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Staging.json b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.json b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.json new file mode 100644 index 00000000..97b325d7 --- /dev/null +++ b/Api.Logging.Log4Net/Api.Logging.Log4Net/appsettings.json @@ -0,0 +1,22 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] + } +} \ No newline at end of file diff --git a/Api.Logging.Log4Net/Dockerfile b/Api.Logging.Log4Net/Dockerfile new file mode 100644 index 00000000..8ba64365 --- /dev/null +++ b/Api.Logging.Log4Net/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Api.Logging.Log4Net/LICENSE b/Api.Logging.Log4Net/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Logging.Log4Net/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Logging.Log4Net/README.md b/Api.Logging.Log4Net/README.md new file mode 100644 index 00000000..374f470e --- /dev/null +++ b/Api.Logging.Log4Net/README.md @@ -0,0 +1,62 @@ +# Api.Logging.Log4Net + +> _Nano API application with Log4Net logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This application demonstrates logging with Log4Net for a API application. Also note the `LogLevelOverrides` configuration, +where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. + +The following endpoint is available for testing: + +| Endpoint | Description | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/logging` | Returns a simple `200 OK` response. Won't log the `.LogDebug(...)` due to configuration `LogLevel=Information`. | +| `http://localhost:8080/api/examples/logging-exception` | Returns a simple `500 Internal Server Error` response. The exception will be logged. | + +> 📖 Learn more about **[Nano.Logging.Log4Net](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Log4Net/README.md#nanologginglog4net)**. + +## Registration +The following logging has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoLogging(); +}) +... +``` + +## Configuration +Configured the application with the necessary logging setup. + +```json +"Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] +} +``` diff --git a/Api.Logging.Log4Net/icon.png b/Api.Logging.Log4Net/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Logging.Microsoft/.docker/docker-compose.yml b/Api.Logging.Microsoft/.docker/docker-compose.yml new file mode 100644 index 00000000..be89bf7c --- /dev/null +++ b/Api.Logging.Microsoft/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.logging.microsoft: + image: api.logging.microsoft + hostname: api-logging-microsoft + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Logging.Microsoft + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Logging.Microsoft/.dockerignore b/Api.Logging.Microsoft/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Logging.Microsoft/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Logging.Microsoft/.github/config/slack.yml b/Api.Logging.Microsoft/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Logging.Microsoft/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml b/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..c4343328 --- /dev/null +++ b/Api.Logging.Microsoft/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Logging.Microsoft + IMAGE_NAME: api.logging.microsoft + SERVICE_NAME: api-logging-microsoft + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Logging.Microsoft/.gitignore b/Api.Logging.Microsoft/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Logging.Microsoft/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Logging.Microsoft/.kubernetes/autoscaler.yaml b/Api.Logging.Microsoft/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Logging.Microsoft/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Logging.Microsoft/.kubernetes/configmap.yaml b/Api.Logging.Microsoft/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Logging.Microsoft/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Logging.Microsoft/.kubernetes/deployment.yaml b/Api.Logging.Microsoft/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Logging.Microsoft/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Logging.Microsoft/.kubernetes/service.yaml b/Api.Logging.Microsoft/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Logging.Microsoft/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Properties/DoNotParallelize.cs b/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Tests.Api.Logging.Microsoft.csproj b/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Tests.Api.Logging.Microsoft.csproj new file mode 100644 index 00000000..8ecfe29d --- /dev/null +++ b/Api.Logging.Microsoft/.tests/Tests.Api.Logging.Microsoft/Tests.Api.Logging.Microsoft.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj b/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj new file mode 100644 index 00000000..a284b2dc --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft.Models/Api.Logging.Microsoft.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft.sln b/Api.Logging.Microsoft/Api.Logging.Microsoft.sln new file mode 100644 index 00000000..e6202494 --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft.sln @@ -0,0 +1,147 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Microsoft.Models", "Api.Logging.Microsoft.Models\Api.Logging.Microsoft.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Microsoft", "Api.Logging.Microsoft\Api.Logging.Microsoft.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Logging.Microsoft", ".tests\Tests.Api.Logging.Microsoft\Tests.Api.Logging.Microsoft.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Microsoft", "..\..\Nano.Library\Nano.Logging.Microsoft\Nano.Logging.Microsoft.csproj", "{FB775619-7CDE-E029-14F8-7C22944D6DC9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {68215D72-880F-683C-BFEE-ED19A425A5F2} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {FB775619-7CDE-E029-14F8-7C22944D6DC9} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Api.Logging.Microsoft.csproj b/Api.Logging.Microsoft/Api.Logging.Microsoft/Api.Logging.Microsoft.csproj new file mode 100644 index 00000000..5d7ac8da --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/Api.Logging.Microsoft.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Controllers/ExamplesController.cs b/Api.Logging.Microsoft/Api.Logging.Microsoft/Controllers/ExamplesController.cs new file mode 100644 index 00000000..1406e8b5 --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/Controllers/ExamplesController.cs @@ -0,0 +1,60 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Logging.Microsoft.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Logging Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("logging")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LoggingAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + this.Logger + .LogDebug("Debug"); + + this.Logger + .LogInformation("Information"); + + this.Logger + .LogWarning("Warning"); + + this.Logger + .LogError("Error"); + + return this.Ok("logging"); + } + + /// + /// Logging Exception Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("logging-exception")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LoggingExceptionAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new Exception("test error"); + } +} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local b/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local new file mode 100644 index 00000000..adaac8c3 --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Program.cs b/Api.Logging.Microsoft/Api.Logging.Microsoft/Program.cs new file mode 100644 index 00000000..93786083 --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Api; +using Nano.Logging.Extensions; +using Nano.Logging.Microsoft; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoLogging(); + }) + .Build() + .Run(); diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/Properties/InternalsVisibleTo.cs b/Api.Logging.Microsoft/Api.Logging.Microsoft/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..1916d193 --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Logging.Microsoft")] \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Development.json b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Production.json b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Staging.json b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.json b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.json new file mode 100644 index 00000000..97b325d7 --- /dev/null +++ b/Api.Logging.Microsoft/Api.Logging.Microsoft/appsettings.json @@ -0,0 +1,22 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] + } +} \ No newline at end of file diff --git a/Api.Logging.Microsoft/Dockerfile b/Api.Logging.Microsoft/Dockerfile new file mode 100644 index 00000000..d77a0004 --- /dev/null +++ b/Api.Logging.Microsoft/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Api.Logging.Microsoft/LICENSE b/Api.Logging.Microsoft/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Logging.Microsoft/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Logging.Microsoft/README.md b/Api.Logging.Microsoft/README.md new file mode 100644 index 00000000..525ba003 --- /dev/null +++ b/Api.Logging.Microsoft/README.md @@ -0,0 +1,62 @@ +# Api.Logging.Microsoft + +> _Nano API application with Microsoft logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This application demonstrates logging with Microsoft for a API application. Also note the `LogLevelOverrides` configuration, +where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. + +The following endpoint is available for testing: + +| Endpoint | Description | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/logging` | Returns a simple `200 OK` response. Won't log the `.LogDebug(...)` due to configuration `LogLevel=Information`. | +| `http://localhost:8080/api/examples/logging-exception` | Returns a simple `500 Internal Server Error` response. The exception will be logged. | + +> 📖 Learn more about **[Nano.Logging.Microsoft](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Microsoft/README.md#nanologgingmicrosoft)**. + +## Registration +The following logging has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoLogging(); +}) +... +``` + +## Configuration +Configured the application with the necessary logging setup. + +```json +"Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] +} +``` diff --git a/Api.Logging.Microsoft/icon.png b/Api.Logging.Microsoft/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Logging.NLog/.docker/docker-compose.yml b/Api.Logging.NLog/.docker/docker-compose.yml new file mode 100644 index 00000000..0922a6b5 --- /dev/null +++ b/Api.Logging.NLog/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.logging.nlog: + image: api.logging.nlog + hostname: api-logging-nlog + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Logging.NLog + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Logging.NLog/.dockerignore b/Api.Logging.NLog/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Logging.NLog/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Logging.NLog/.github/config/slack.yml b/Api.Logging.NLog/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Logging.NLog/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Logging.NLog/.github/workflows/build-and-deploy.yml b/Api.Logging.NLog/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..16faef4c --- /dev/null +++ b/Api.Logging.NLog/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Logging.NLog + IMAGE_NAME: api.logging.nlog + SERVICE_NAME: api-logging-nlog + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Logging.NLog/.gitignore b/Api.Logging.NLog/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Logging.NLog/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Logging.NLog/.kubernetes/autoscaler.yaml b/Api.Logging.NLog/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Logging.NLog/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Logging.NLog/.kubernetes/configmap.yaml b/Api.Logging.NLog/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Logging.NLog/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Logging.NLog/.kubernetes/deployment.yaml b/Api.Logging.NLog/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Logging.NLog/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Logging.NLog/.kubernetes/service.yaml b/Api.Logging.NLog/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Logging.NLog/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Properties/DoNotParallelize.cs b/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Tests.Api.Logging.NLog.csproj b/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Tests.Api.Logging.NLog.csproj new file mode 100644 index 00000000..63c37b06 --- /dev/null +++ b/Api.Logging.NLog/.tests/Tests.Api.Logging.NLog/Tests.Api.Logging.NLog.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj b/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj new file mode 100644 index 00000000..8989e105 --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog.Models/Api.Logging.NLog.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog.sln b/Api.Logging.NLog/Api.Logging.NLog.sln new file mode 100644 index 00000000..d5a0c759 --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog.sln @@ -0,0 +1,147 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.NLog.Models", "Api.Logging.NLog.Models\Api.Logging.NLog.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.NLog", "Api.Logging.NLog\Api.Logging.NLog.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Logging.NLog", ".tests\Tests.Api.Logging.NLog\Tests.Api.Logging.NLog.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.NLog", "..\..\Nano.Library\Nano.Logging.NLog\Nano.Logging.NLog.csproj", "{FB775619-7CDE-E029-14F8-7C22944D6DC9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {68215D72-880F-683C-BFEE-ED19A425A5F2} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {FB775619-7CDE-E029-14F8-7C22944D6DC9} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Logging.NLog/Api.Logging.NLog/Api.Logging.NLog.csproj b/Api.Logging.NLog/Api.Logging.NLog/Api.Logging.NLog.csproj new file mode 100644 index 00000000..5a8b6ea6 --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/Api.Logging.NLog.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/Controllers/ExamplesController.cs b/Api.Logging.NLog/Api.Logging.NLog/Controllers/ExamplesController.cs new file mode 100644 index 00000000..85dfb8bb --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/Controllers/ExamplesController.cs @@ -0,0 +1,60 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Logging.NLog.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Logging Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("logging")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LoggingAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + this.Logger + .LogDebug("Debug"); + + this.Logger + .LogInformation("Information"); + + this.Logger + .LogWarning("Warning"); + + this.Logger + .LogError("Error"); + + return this.Ok("logging"); + } + + /// + /// Logging Exception Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("logging-exception")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LoggingExceptionAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new Exception("test error"); + } +} \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local b/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local new file mode 100644 index 00000000..e47db8d4 --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Logging.NLog.dll"] \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/Program.cs b/Api.Logging.NLog/Api.Logging.NLog/Program.cs new file mode 100644 index 00000000..659ee02b --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Api; +using Nano.Logging.Extensions; +using Nano.Logging.NLog; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoLogging(); + }) + .Build() + .Run(); diff --git a/Api.Logging.NLog/Api.Logging.NLog/Properties/InternalsVisibleTo.cs b/Api.Logging.NLog/Api.Logging.NLog/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..ab204547 --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Logging.NLog")] \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Development.json b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Production.json b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/appsettings.Staging.json b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.NLog/Api.Logging.NLog/appsettings.json b/Api.Logging.NLog/Api.Logging.NLog/appsettings.json new file mode 100644 index 00000000..97b325d7 --- /dev/null +++ b/Api.Logging.NLog/Api.Logging.NLog/appsettings.json @@ -0,0 +1,22 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] + } +} \ No newline at end of file diff --git a/Api.Logging.NLog/Dockerfile b/Api.Logging.NLog/Dockerfile new file mode 100644 index 00000000..f7f28f9d --- /dev/null +++ b/Api.Logging.NLog/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Logging.NLog.dll"] \ No newline at end of file diff --git a/Api.Logging.NLog/LICENSE b/Api.Logging.NLog/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Logging.NLog/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Logging.NLog/README.md b/Api.Logging.NLog/README.md new file mode 100644 index 00000000..df4ac65b --- /dev/null +++ b/Api.Logging.NLog/README.md @@ -0,0 +1,62 @@ +# Api.Logging.NLog + +> _Nano API application with NLog logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This application demonstrates logging with NLog for a API application. Also note the `LogLevelOverrides` configuration, +where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/logging` | Returns a simple `200 OK` response. Won't log the `.LogDebug(...)` due to configuration `LogLevel=Information`. | +| `http://localhost:8080/api/examples/logging-exception` | Returns a simple `500 Internal Server Error` response. The exception will be logged. | + +> 📖 Learn more about **[Nano.Logging.NLog](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.NLog/README.md#nanologgingnlog)**. + +## Registration +The following logging has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoLogging(); +}) +... +``` + +## Configuration +Configured the application with the necessary logging setup. + +```json +"Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] +} +``` diff --git a/Api.Logging.NLog/icon.png b/Api.Logging.NLog/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Logging.Serilog/.docker/docker-compose.yml b/Api.Logging.Serilog/.docker/docker-compose.yml new file mode 100644 index 00000000..c90a936c --- /dev/null +++ b/Api.Logging.Serilog/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.logging.serilog: + image: api.logging.serilog + hostname: api-logging-serilog + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Logging.Serilog + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Logging.Serilog/.dockerignore b/Api.Logging.Serilog/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Logging.Serilog/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Logging.Serilog/.github/config/slack.yml b/Api.Logging.Serilog/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Logging.Serilog/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml b/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..c26ccdfb --- /dev/null +++ b/Api.Logging.Serilog/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Logging.Serilog + IMAGE_NAME: api.logging.serilog + SERVICE_NAME: api-logging-serilog + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Logging.Serilog/.gitignore b/Api.Logging.Serilog/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Logging.Serilog/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Logging.Serilog/.kubernetes/autoscaler.yaml b/Api.Logging.Serilog/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Logging.Serilog/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Logging.Serilog/.kubernetes/configmap.yaml b/Api.Logging.Serilog/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Logging.Serilog/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Logging.Serilog/.kubernetes/deployment.yaml b/Api.Logging.Serilog/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Logging.Serilog/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Logging.Serilog/.kubernetes/service.yaml b/Api.Logging.Serilog/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Logging.Serilog/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Properties/DoNotParallelize.cs b/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Tests.Api.Logging.Serilog.csproj b/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Tests.Api.Logging.Serilog.csproj new file mode 100644 index 00000000..9abdeec5 --- /dev/null +++ b/Api.Logging.Serilog/.tests/Tests.Api.Logging.Serilog/Tests.Api.Logging.Serilog.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj b/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj new file mode 100644 index 00000000..d1fa3771 --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog.Models/Api.Logging.Serilog.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog.sln b/Api.Logging.Serilog/Api.Logging.Serilog.sln new file mode 100644 index 00000000..d9108a28 --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog.sln @@ -0,0 +1,147 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Serilog.Models", "Api.Logging.Serilog.Models\Api.Logging.Serilog.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Logging.Serilog", "Api.Logging.Serilog\Api.Logging.Serilog.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Logging.Serilog", ".tests\Tests.Api.Logging.Serilog\Tests.Api.Logging.Serilog.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Serilog", "..\..\Nano.Library\Nano.Logging.Serilog\Nano.Logging.Serilog.csproj", "{FB775619-7CDE-E029-14F8-7C22944D6DC9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB775619-7CDE-E029-14F8-7C22944D6DC9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {68215D72-880F-683C-BFEE-ED19A425A5F2} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {FB775619-7CDE-E029-14F8-7C22944D6DC9} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Api.Logging.Serilog.csproj b/Api.Logging.Serilog/Api.Logging.Serilog/Api.Logging.Serilog.csproj new file mode 100644 index 00000000..1ee7daba --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/Api.Logging.Serilog.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Controllers/ExamplesController.cs b/Api.Logging.Serilog/Api.Logging.Serilog/Controllers/ExamplesController.cs new file mode 100644 index 00000000..86db1ecb --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/Controllers/ExamplesController.cs @@ -0,0 +1,60 @@ +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.Logging.Serilog.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Logging Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("logging")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LoggingAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + this.Logger + .LogDebug("Debug"); + + this.Logger + .LogInformation("Information"); + + this.Logger + .LogWarning("Warning"); + + this.Logger + .LogError("Error"); + + return this.Ok("logging"); + } + + /// + /// Logging Exception Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("logging-exception")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task LoggingExceptionAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + throw new Exception("test error"); + } +} \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local b/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local new file mode 100644 index 00000000..006fb412 --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Program.cs b/Api.Logging.Serilog/Api.Logging.Serilog/Program.cs new file mode 100644 index 00000000..d2ab08f7 --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Api; +using Nano.Logging.Extensions; +using Nano.Logging.Serilog; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoLogging(); + }) + .Build() + .Run(); diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/Properties/InternalsVisibleTo.cs b/Api.Logging.Serilog/Api.Logging.Serilog/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..ca569d77 --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Logging.Serilog")] \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Development.json b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Production.json b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Staging.json b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.json b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.json new file mode 100644 index 00000000..97b325d7 --- /dev/null +++ b/Api.Logging.Serilog/Api.Logging.Serilog/appsettings.json @@ -0,0 +1,22 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + }, + "Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] + } +} \ No newline at end of file diff --git a/Api.Logging.Serilog/Dockerfile b/Api.Logging.Serilog/Dockerfile new file mode 100644 index 00000000..e211ed1c --- /dev/null +++ b/Api.Logging.Serilog/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Api.Logging.Serilog/LICENSE b/Api.Logging.Serilog/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Logging.Serilog/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Logging.Serilog/README.md b/Api.Logging.Serilog/README.md new file mode 100644 index 00000000..e52dcf19 --- /dev/null +++ b/Api.Logging.Serilog/README.md @@ -0,0 +1,62 @@ +# Api.Logging.Serilog + +> _Nano API application with serilog logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This application demonstrates logging with Serilog for a API application. Also note the `LogLevelOverrides` configuration, +where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/logging` | Returns a simple `200 OK` response. Won't log the `.LogDebug(...)` due to configuration `LogLevel=Information`. | +| `http://localhost:8080/api/examples/logging-exception` | Returns a simple `500 Internal Server Error` response. The exception will be logged. | + +> 📖 Learn more about **[Nano.Logging.Serilog](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Serilog/README.md#nanologgingserilog)**. + +## Registration +The following logging has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoLogging(); +}) +... +``` + +## Configuration +Configured the application with the necessary logging setup. + +```json +"Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] +} +``` diff --git a/Api.Logging.Serilog/icon.png b/Api.Logging.Serilog/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.MultipartJson/.docker/docker-compose.yml b/Api.MultipartJson/.docker/docker-compose.yml new file mode 100644 index 00000000..c4c85d9e --- /dev/null +++ b/Api.MultipartJson/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.multipartjson: + image: api.multipartjson + hostname: api-multipartjson + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.MultipartJson + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.MultipartJson/.dockerignore b/Api.MultipartJson/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.MultipartJson/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.MultipartJson/.github/config/slack.yml b/Api.MultipartJson/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.MultipartJson/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.MultipartJson/.github/workflows/build-and-deploy.yml b/Api.MultipartJson/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..8f3918cf --- /dev/null +++ b/Api.MultipartJson/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.MultipartJson + IMAGE_NAME: api.multipartjson + SERVICE_NAME: api-multipartjson + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.MultipartJson/.gitignore b/Api.MultipartJson/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.MultipartJson/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.MultipartJson/.kubernetes/autoscaler.yaml b/Api.MultipartJson/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.MultipartJson/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.MultipartJson/.kubernetes/configmap.yaml b/Api.MultipartJson/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.MultipartJson/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.MultipartJson/.kubernetes/deployment.yaml b/Api.MultipartJson/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.MultipartJson/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.MultipartJson/.kubernetes/service.yaml b/Api.MultipartJson/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.MultipartJson/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Properties/DoNotParallelize.cs b/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Tests.Api.MultipartJson.csproj b/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Tests.Api.MultipartJson.csproj new file mode 100644 index 00000000..96d385fd --- /dev/null +++ b/Api.MultipartJson/.tests/Tests.Api.MultipartJson/Tests.Api.MultipartJson.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj b/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson.Models/Api.MultipartJson.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson.sln b/Api.MultipartJson/Api.MultipartJson.sln new file mode 100644 index 00000000..e22fe2c9 --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.MultipartJson.Models", "Api.MultipartJson.Models\Api.MultipartJson.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.MultipartJson", "Api.MultipartJson\Api.MultipartJson.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.MultipartJson", ".tests\Tests.Api.MultipartJson\Tests.Api.MultipartJson.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.MultipartJson/Api.MultipartJson/Api.MultipartJson.csproj b/Api.MultipartJson/Api.MultipartJson/Api.MultipartJson.csproj new file mode 100644 index 00000000..d9549228 --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/Api.MultipartJson.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/Controllers/ExamplesController.cs b/Api.MultipartJson/Api.MultipartJson/Controllers/ExamplesController.cs new file mode 100644 index 00000000..c88e36b9 --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/Controllers/ExamplesController.cs @@ -0,0 +1,42 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.MultipartJson.Controllers.Requests; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Annotations; +using Nano.App.Api.Controllers; +using Newtonsoft.Json; + +namespace Api.MultipartJson.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Multipart Json Action. + /// + /// The file. + /// The json body + /// The cancellation token. + /// A message. + /// Success. + [HttpPost] + [Route("multipart-json")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task MultipartJsonAsync(IFormFile file, [Required][FromFormBody]JsonBody body, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + Console.WriteLine(file.FileName); + Console.WriteLine(JsonConvert.SerializeObject(body)); + + return this.Ok("multipart-json"); + } +} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/Controllers/Requests/JsonBody.cs b/Api.MultipartJson/Api.MultipartJson/Controllers/Requests/JsonBody.cs new file mode 100644 index 00000000..2c2ffba9 --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/Controllers/Requests/JsonBody.cs @@ -0,0 +1,15 @@ +using System.ComponentModel.DataAnnotations; + +namespace Api.MultipartJson.Controllers.Requests; + +/// +/// Json Body. +/// +public class JsonBody +{ + /// + /// Text. + /// + [Required] + public string Text { get; set; } = null!; +} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local b/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local new file mode 100644 index 00000000..8d1d3884 --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.MultipartJson.dll"] \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/Program.cs b/Api.MultipartJson/Api.MultipartJson/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.MultipartJson/Api.MultipartJson/Properties/InternalsVisibleTo.cs b/Api.MultipartJson/Api.MultipartJson/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..ccb78b5f --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.MultipartJson")] \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/appsettings.Development.json b/Api.MultipartJson/Api.MultipartJson/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/appsettings.Production.json b/Api.MultipartJson/Api.MultipartJson/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/appsettings.Staging.json b/Api.MultipartJson/Api.MultipartJson/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.MultipartJson/Api.MultipartJson/appsettings.json b/Api.MultipartJson/Api.MultipartJson/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.MultipartJson/Api.MultipartJson/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.MultipartJson/Dockerfile b/Api.MultipartJson/Dockerfile new file mode 100644 index 00000000..efb0b01a --- /dev/null +++ b/Api.MultipartJson/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.MultipartJson.dll"] \ No newline at end of file diff --git a/Api.MultipartJson/LICENSE b/Api.MultipartJson/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.MultipartJson/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.MultipartJson/README.md b/Api.MultipartJson/README.md new file mode 100644 index 00000000..b4c35ee4 --- /dev/null +++ b/Api.MultipartJson/README.md @@ -0,0 +1,30 @@ +# Api.MultipartJson + +> _Nano API application using request multipart json._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example simply shows uploading a file together with a JSON body. Observe the filename of the file is written to console, as well as the json body passed. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ---------------------------------------------------- | -------------------------------------- | +| `http://localhost:8080/api/examples/multipart-json` | Returns a simple `200 OK` response. | + +> 📖 Learn more about **[Nano Request Multipart JSON](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#request-multipart-json)**. diff --git a/Api.MultipartJson/icon.png b/Api.MultipartJson/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.yml b/Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.yml new file mode 100644 index 00000000..4d629227 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.docker/docker-compose.yml @@ -0,0 +1,20 @@ +services: + api.policyheaders.contentsecuritypolicy: + image: api.policyheaders.contentsecuritypolicy + hostname: api-policyheaders-contentsecuritypolicy + restart: on-failure + ports: + - 8080:8080 + - 4443:4443 + build: + context: ../Api.PolicyHeaders.ContentSecurityPolicy + dockerfile: "Dockerfile.Local" + volumes: + - ../:/root/.dotnet/https + networks: + - network + +networks: + network: + name: network + driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.dockerignore b/Api.PolicyHeaders.ContentSecurityPolicy/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.github/config/slack.yml b/Api.PolicyHeaders.ContentSecurityPolicy/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..c97c19fa --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.github/workflows/build-and-deploy.yml @@ -0,0 +1,205 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.PolicyHeaders.ContentSecurityPolicy + IMAGE_NAME: api.policyheaders.contentsecuritypolicy + SERVICE_NAME: api-policyheaders-contentsecuritypolicy + SUB_DOMAIN_NAME: nano + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query "[].name" -o json | ConvertFrom-Json + + $env:ROUTE_HOST_NAMES = ( + $zoneNames | ForEach-Object { + " - $env:SUB_DOMAIN_NAME.$_" + } + ) -join "`n" + + $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/grafana-httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-80.tmp.yaml; + kubectl apply -f .kubernetes/grafana-httproute-80.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/grafana-httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-443.tmp.yaml; + kubectl apply -f .kubernetes/grafana-httproute-443.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.gitignore b/Api.PolicyHeaders.ContentSecurityPolicy/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-443.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-443.yaml new file mode 100644 index 00000000..65b97a51 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-443.yaml @@ -0,0 +1,18 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %SERVICE_NAME%-route + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + hostnames: +%ROUTE_HOST_NAMES% + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: %SERVICE_NAME% + port: 8080 \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-80.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-80.yaml new file mode 100644 index 00000000..f29775aa --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/httproute-80.yaml @@ -0,0 +1,17 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %APP_NAME%-route-80 + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + sectionName: http + hostnames: +%ROUTE_HOST_NAMES% + rules: + - filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/service.yaml b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj b/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj new file mode 100644 index 00000000..7d531b1d --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/.tests/Tests.Api.PolicyHeaders.ContentSecurityPolicy/Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj new file mode 100644 index 00000000..890be2f1 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.Models/Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.sln b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.sln new file mode 100644 index 00000000..39b83d4c --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.sln @@ -0,0 +1,137 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + localhost.pfx = localhost.pfx + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\httproute-443.yaml = .kubernetes\httproute-443.yaml + .kubernetes\httproute-80.yaml = .kubernetes\httproute-80.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ContentSecurityPolicy.Models", "Api.PolicyHeaders.ContentSecurityPolicy.Models\Api.PolicyHeaders.ContentSecurityPolicy.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ContentSecurityPolicy", "Api.PolicyHeaders.ContentSecurityPolicy\Api.PolicyHeaders.ContentSecurityPolicy.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.ContentSecurityPolicy", ".tests\Tests.Api.PolicyHeaders.ContentSecurityPolicy\Tests.Api.PolicyHeaders.ContentSecurityPolicy.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.csproj b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.csproj new file mode 100644 index 00000000..e11f0b6f --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Controllers/ExamplesController.cs b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Controllers/ExamplesController.cs new file mode 100644 index 00000000..002e4989 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Api.PolicyHeaders.ContentSecurityPolicy.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Csp Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("csp")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task CspAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("csp"); + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local new file mode 100644 index 00000000..c6f98fe9 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 4443 + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentSecurityPolicy.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Program.cs b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Program.cs new file mode 100644 index 00000000..ea31b138 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Api; + +// BUG: We need logging here because otherwise we don't see the logged csp-report + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..e5d9e205 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Hosting.Http")] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json new file mode 100644 index 00000000..23bfb871 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Development.json @@ -0,0 +1,19 @@ +{ + "App": { + "Hosting": { + "http": { + "UseHttpsRedirection": true + }, + "Https": { + "Ports": [ + 4443 + ], + "Certificate": { + "Path": "/root/.dotnet/https/localhost.pfx", + "Password": "password" + }, + "UseHttpsRequired": true + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Production.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Staging.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.json b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.json new file mode 100644 index 00000000..fabe077b --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/appsettings.json @@ -0,0 +1,76 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HttpPolicyHeaders": { + "Csp": { + "ReportOnly": false, + "UpgradeInsecureRequests": true, + "Defaults": { + "IsNone": false, + "IsSelf": true, + "Sources": [ + "https://localhost" + ] + }, + "Scripts": { + "IsNone": false, + "IsSelf": true, + "IsUnsafeInline": false, + "IsUnsafeEval": false, + "IsUnsafeWasmEval": false, + "IsTrustedTypesEval": false, + "IsUnsafeHashes": false, + "StrictDynamic": false, + "UnsafeHashedAttributes": false, + "UnsafeAllowRedirects": false, + "InlineSpeculationRules": false, + "Sources": [ + ], + "Nonces": [ + ], + "Hashes": [ + ], + "RequireTrustedTypes": false, + "RequireSri": false, + "ReportSample": true + }, + "Styles": { + "IsNone": false, + "IsSelf": true, + "IsUnsafeInline": false, + "IsUnsafeHashes": false, + "Sources": [ + ], + "Nonces": [ + ], + "Hashes": [ + ], + "RequireSri": false, + "ReportSample": true + }, + "PermissionsPolicy": { + "Gamepad": { + "IsNone": false, + "IsSelf": true, + "Sources": [ + ] + } + }, + "ReportTo": { + "Group": "csp-reports", + "MaxAge": "60", + "Endpoints": [ + ] + } + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/wwwroot/csp-violation.html b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/wwwroot/csp-violation.html new file mode 100644 index 00000000..0f8f47aa --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Api.PolicyHeaders.ContentSecurityPolicy/wwwroot/csp-violation.html @@ -0,0 +1,17 @@ + + + + + CSP Violation Test + + +

CSP Violation Test

+ +

This page will trigger a CSP violation by loading an external script and by using eval.

+ + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile b/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile new file mode 100644 index 00000000..0bcd6f21 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentSecurityPolicy.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/LICENSE b/Api.PolicyHeaders.ContentSecurityPolicy/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/README.md b/Api.PolicyHeaders.ContentSecurityPolicy/README.md new file mode 100644 index 00000000..a8c3b1b0 --- /dev/null +++ b/Api.PolicyHeaders.ContentSecurityPolicy/README.md @@ -0,0 +1,130 @@ +# Api.PolicyHeaders.ContentSecurityPolicy + +> _Nano API application with content security policy (CSP)._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Hosting.Https](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Hosting.Https)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +Content Security Policy (CSP) supports a wide range of configurations. This example demonstrates a small, representative subset of directives to illustrate +how CSP is configured and applied. You are encouraged to experiment with the settings and inspect the resulting `Content-Security-Policy` response header +to better understand their effects. + +The service is configured to run over HTTPS, as most browsers will not send CSP violation reports to a `csp-report` endpoint over HTTP. +The `Report-To` directive is enabled and configured to send reports to Nano’s default endpoint at `/csp/report-to`. + +> ⚠️ Browsers typically batch and delay CSP reports, so it may take up to a minute—or longer—before reports are sent. + +To observe CSP violations in action, load the provided `csp-violation.html` file and inspect the browser’s reporting behavior. + +| Endpoint | Description | +| ---------------------------------------- | --------------------------------------------------------------- | +| `http://localhost:8080/api/examples/csp` | Returns a `200 OK` response including the CSP response header. | + +> 📖 Learn more about **[Nano Content Security Options](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#content-type-options)**. + +## Configuration +Added content security policy header configuration. + +```json +"App": { + "HttpPolicyHeaders": { + "Csp": { + "ReportOnly": false, + "UpgradeInsecureRequests": true, + "Defaults": { + "IsNone": false, + "IsSelf": true, + "Sources": [ + "https://localhost" + ] + }, + "Scripts": { + "IsNone": false, + "IsSelf": true, + "IsUnsafeInline": false, + "IsUnsafeEval": false, + "IsUnsafeWasmEval": false, + "IsTrustedTypesEval": false, + "IsUnsafeHashes": false, + "StrictDynamic": false, + "UnsafeHashedAttributes": false, + "UnsafeAllowRedirects": false, + "InlineSpeculationRules": false, + "Sources": [ + ], + "Nonces": [ + ], + "Hashes": [ + ], + "RequireTrustedTypes": false, + "RequireSri": false, + "ReportSample": true + }, + "Styles": { + "IsNone": false, + "IsSelf": true, + "IsUnsafeInline": false, + "IsUnsafeHashes": false, + "Sources": [ + ], + "Nonces": [ + ], + "Hashes": [ + ], + "RequireSri": false, + "ReportSample": true + }, + "StylesElem": { + "IsNone": false, + "IsSelf": true, + "IsUnsafeInline": false, + "Sources": [ + ], + "Nonces": [ + ], + "Hashes": [ + ], + "ReportSample": true + }, + "StylesAttr": { + "IsNone": false, + "IsSelf": true, + "IsUnsafeInline": false, + "IsUnsafeHashes": false, + "Sources": [ + ], + "ReportSample": true + }, + "PermissionsPolicy": { + "Gamepad": { + "IsNone": false, + "IsSelf": true, + "Sources": [ + ] + } + }, + "ReportTo": { + "Group": "csp-reports", + "MaxAge": "60", + "Endpoints": [ + ] + } + } + } +} +``` diff --git a/Api.PolicyHeaders.ContentSecurityPolicy/icon.png b/Api.PolicyHeaders.ContentSecurityPolicy/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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
?}-`UAua z$|bV$LW!(!2F-yX!hrvgm^PtArZo(aX_-+}7~9`d?5rSgE)jZwAwp>w0T}ClX@>bx z=nc*HCI7IQg7EC^xSi` zsh$H1d#kBqFmjWWI+-2xxn^#;s{h@=q|gCJluh!Pw-eF_rONGd>@q%yRn;^mZCTyH z*26kLB~H(0Gltgp(KQ$%ReX5$MPV|ll+XnthXCJTu5OoY{BHBm;#hnsKKBV=>#UW( zw9u6%l~Y{zB6m1YW!#!8NH}K$HJ#LP56kX~i&#nl;g4?eTu|T5!LHTwh8tY=Fs`VJ zb?Kxr?SFiU}Y`8lk86mUVIXpbkK>-+JC=Sxm5NdCHSUJB9?q^`1W$(T{e%-snnLx zO5KFchJfr5BjyAhN`p*`Cn}tcx?F8dLPCt^q(5s31yMEkbPEIK@Lw&ir>&Z2`q-wl zsD#)0*SO7b`fch(ik40~$PF0sZ8ORKn)g1o-Cm@#iT>(QQpJ5KFUXRg^x#G#*X6|m<`(ti3*x=1Irq*+c~4co zhg&IdsQbaA#xqvwW4!hQBgM~LU$Mz?+!FlDw0~TE-vl|>QEfu9#B}lVh}7Jtn*>s8 zUite2{w1sx$`Sn77XkblNLmyO>9Wg`cG$9a@=J@_yYq+=3~Vc3i@hh zyOd^KWC)u`e}#0o-@~g9aB*BGD>6=28;^pqkU~RdyL49)8clm_LR$9`H(*<`1hj^puHQE**9*~Rr zP7F<6xLme0%N@}V9e-&zZGAGMquzRzj@s6Nq>e1o^dBIGM)LK|1-mK ziIDFYB4h^)0vN~IzY6I8gMYV&N7=G*zUNo)-zP#^9qzR+c}J`)@L93Ef)2ggDdaPt2vGBzc#BZji$}jwpm#Q9fhD0G_ zc^f?)UD%4YpDolwcijh{oBf$9SdsXb?!|Vh94_d?1huw78#Qy9{q$8kj*n5)Gx-y~ zI>u&v8yPirA>_{8I5{~*nAv1eYvW=%WuTr?4qJvLq({*o%UocV@azU zaj#1h@79FpSMQXeUpIR@rrR-({nWx~rg713#k}io_ci}izM<*Msjx9|Wc+%AWJ`o9 zCor5Ft`j@hX0kn!C|SDwz0Rdn@fJZ5n0j{#;Ov89Y|K7Z)K;D&e%IjJ6hgscs2mU? z4lcMUldRVglWq#X|1w*_)hYf|(T2tMJbFA@0mWl2g7p+`E)%Qt+QMGrU(TW{i4GV{ zNuq$7Akx)fe%(L%QL?OOyk;cW>C<14EelmK%;(%Z^Y;dV%TVeb`gqT~v}Vh<8VfNT zuD%zUiKq4)ES6q{KP>u@xhSACOzh0Um_n5r^mf!b;NRp@BCEsJ?+91qD)st+j%660 z(1BQUcja)n>L?u&njV8?3}i}NZcIL;q?$2^hxIc9M4wfP>JFDr~B zlFM12@c=`XoS`I#BlPcoXX+O92^}>IGF4_@x4g58cDh(M_wJ8Yf>Zt7hj5kx*M4np zBYPGkvA7R+9y~ct2aFPL6XY*=IBiHxZM9E#bC{=@y1B-^?nT{R>SICc_Pa^H9yXaN zx^)GLrI8cGy&4C+lS4OXyApuLe%%)A)e z;_kiFYvU@$P1zo6JC#xI*t00gYZ_`A(3PUlCvjpo!mOJQ`ttv(TLjkw88@WD8J zGXOvp&EDt*dGwraDSfD?Y-7!8aNh8f0$Itl^vLlKjgUEXXr83t@WQ$-mF8yZgtQ%j+U>*9qY?V|RtA{s6P QtUG7zSmhZ)eg(jP05{mZ`~Uy| literal 0 HcmV?d00001 diff --git a/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.dcproj b/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.dcproj new file mode 100644 index 00000000..d9e8e500 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.dcproj @@ -0,0 +1,13 @@ + + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.yml b/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.yml new file mode 100644 index 00000000..a88b70bc --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.policyheaders.contenttypeoptions: + image: api.policyheaders.contenttypeoptions + hostname: api-policyheaders-contenttypeoptions + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.PolicyHeaders.ContentTypeOptions + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.dockerignore b/Api.PolicyHeaders.ContentTypeOptions/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.ContentTypeOptions/.github/config/slack.yml b/Api.PolicyHeaders.ContentTypeOptions/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..4010720a --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.PolicyHeaders.ContentTypeOptions + IMAGE_NAME: api.policyheaders.contenttypeoptions + SERVICE_NAME: api-policyheaders-contenttypeoptions + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.gitignore b/Api.PolicyHeaders.ContentTypeOptions/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/service.yaml b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Tests.Api.PolicyHeaders.ContentTypeOptions.csproj b/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Tests.Api.PolicyHeaders.ContentTypeOptions.csproj new file mode 100644 index 00000000..271fed39 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/.tests/Tests.Api.PolicyHeaders.ContentTypeOptions/Tests.Api.PolicyHeaders.ContentTypeOptions.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj new file mode 100644 index 00000000..890be2f1 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.Models/Api.PolicyHeaders.ContentTypeOptions.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.sln b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.sln new file mode 100644 index 00000000..69f63482 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ContentTypeOptions.Models", "Api.PolicyHeaders.ContentTypeOptions.Models\Api.PolicyHeaders.ContentTypeOptions.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ContentTypeOptions", "Api.PolicyHeaders.ContentTypeOptions\Api.PolicyHeaders.ContentTypeOptions.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.ContentTypeOptions", ".tests\Tests.Api.PolicyHeaders.ContentTypeOptions\Tests.Api.PolicyHeaders.ContentTypeOptions.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.csproj b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.csproj new file mode 100644 index 00000000..971bbf26 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Controllers/ExamplesController.cs b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Controllers/ExamplesController.cs new file mode 100644 index 00000000..8e3de49e --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.PolicyHeaders.ContentTypeOptions.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Content Type Options Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("nosniff")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task ContentTypeOptionsAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("nosniff"); + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local new file mode 100644 index 00000000..c7050317 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentTypeOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Program.cs b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..1013f4a7 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.ContentTypeOptions")] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Development.json b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Production.json b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Staging.json b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.json b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.json new file mode 100644 index 00000000..82bc5f42 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/appsettings.json @@ -0,0 +1,18 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HttpPolicyHeaders": { + "ContentType": { + "NoSniff": true + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation-wrong-type.txt b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation-wrong-type.txt new file mode 100644 index 00000000..22c276ec --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation-wrong-type.txt @@ -0,0 +1 @@ +console.log("This should NOT execute if content-type nosniff is enabled!"); \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation.html b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation.html new file mode 100644 index 00000000..f21353e5 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Api.PolicyHeaders.ContentTypeOptions/wwwroot/content-type-sniff-violation.html @@ -0,0 +1,18 @@ + + + + + Content-Type Nosniff Test + + +

Content-Type Nosniff Test

+ +

This page will attempt to load a script served with the wrong Content-Type.

+ + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/Dockerfile b/Api.PolicyHeaders.ContentTypeOptions/Dockerfile new file mode 100644 index 00000000..fddba199 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ContentTypeOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/LICENSE b/Api.PolicyHeaders.ContentTypeOptions/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.ContentTypeOptions/README.md b/Api.PolicyHeaders.ContentTypeOptions/README.md new file mode 100644 index 00000000..8e11728e --- /dev/null +++ b/Api.PolicyHeaders.ContentTypeOptions/README.md @@ -0,0 +1,41 @@ +# Api.PolicyHeaders.ContentTypeOptions + +> _Nano API application with content type options._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +To observe content-type sniffing in action, load the `content-type-sniff-violation.html` file and see the browser block execution of the script +served with an incorrect `.txt` content-type. + +| Endpoint | Description | +| -------------------------------------------- | ---------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/nosniff` | Returns a `200 OK` response including the Content-Type `nosniff` response header. | + +> 📖 Learn more about **[Nano Content Type Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#content-type-options)**. + +## Configuration +```json +"App": { + "HttpPolicyHeaders": { + "ContentType": { + "NoSniff": true + } + } +} +``` diff --git a/Api.PolicyHeaders.ContentTypeOptions/icon.png b/Api.PolicyHeaders.ContentTypeOptions/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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
+ + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.docker/docker-compose.yml b/Api.PolicyHeaders.Cors/.docker/docker-compose.yml new file mode 100644 index 00000000..78322a2c --- /dev/null +++ b/Api.PolicyHeaders.Cors/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.policyheaders.cors: + image: api.policyheaders.cors + hostname: api-policyheaders-cors + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.PolicyHeaders.Cors + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.dockerignore b/Api.PolicyHeaders.Cors/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.PolicyHeaders.Cors/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.Cors/.github/config/slack.yml b/Api.PolicyHeaders.Cors/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.PolicyHeaders.Cors/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..e53037ec --- /dev/null +++ b/Api.PolicyHeaders.Cors/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.PolicyHeaders.Cors + IMAGE_NAME: api.policyheaders.cors + SERVICE_NAME: api-policyheaders-cors + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.gitignore b/Api.PolicyHeaders.Cors/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.PolicyHeaders.Cors/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.Cors/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.PolicyHeaders.Cors/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.PolicyHeaders.Cors/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.PolicyHeaders.Cors/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.PolicyHeaders.Cors/.kubernetes/service.yaml b/Api.PolicyHeaders.Cors/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.PolicyHeaders.Cors/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Tests.Api.PolicyHeaders.Cors.csproj b/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Tests.Api.PolicyHeaders.Cors.csproj new file mode 100644 index 00000000..45848230 --- /dev/null +++ b/Api.PolicyHeaders.Cors/.tests/Tests.Api.PolicyHeaders.Cors/Tests.Api.PolicyHeaders.Cors.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj new file mode 100644 index 00000000..890be2f1 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.Models/Api.PolicyHeaders.Cors.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.sln b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.sln new file mode 100644 index 00000000..06c264a5 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Cors.Models", "Api.PolicyHeaders.Cors.Models\Api.PolicyHeaders.Cors.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Cors", "Api.PolicyHeaders.Cors\Api.PolicyHeaders.Cors.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.Cors", ".tests\Tests.Api.PolicyHeaders.Cors\Tests.Api.PolicyHeaders.Cors.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.csproj b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.csproj new file mode 100644 index 00000000..4959095d --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Controllers/ExamplesController.cs b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Controllers/ExamplesController.cs new file mode 100644 index 00000000..1b11b762 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.PolicyHeaders.Cors.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Cors Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("cors")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task CorsAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("cors"); + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local new file mode 100644 index 00000000..27de0430 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Cors.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Program.cs b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..b047e048 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.Cors")] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Development.json b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Production.json b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Staging.json b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.json b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.json new file mode 100644 index 00000000..5e89c5f7 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/appsettings.json @@ -0,0 +1,36 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HttpPolicyHeaders": { + "Cors": { + "AllowedOrigins": [ + "http://localhost:8080" + ], + "AllowedHeaders": [ + "Content-Type" + ], + "AllowedMethods": [ + "GET", + "POST", + "OPTIONS" + ], + "AllowCredentials": false, + "Origin": { + "EmbedderPolicy": "UnsafeNone", + "OpenerPolicy": "SameOriginAllowPopups", + "ResourcePolicy": "SameOrigin" + }, + "ExposedHeaders": [ + ] + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-credetntials-not-alloweed.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-credetntials-not-alloweed.html new file mode 100644 index 00000000..9826d21b --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-credetntials-not-alloweed.html @@ -0,0 +1,19 @@ + + + + + CORS Credentials Violation + + +

CORS Credentials Violation

+ + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-header-not-allowed.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-header-not-allowed.html new file mode 100644 index 00000000..4ae956d6 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-header-not-allowed.html @@ -0,0 +1,22 @@ + + + + + CORS Header Violation + + +

CORS Header Violation

+ + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-method-not-allowed.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-method-not-allowed.html new file mode 100644 index 00000000..9889796c --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-method-not-allowed.html @@ -0,0 +1,19 @@ + + + + + CORS Method Violation + + +

CORS Method Violation

+ + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-origin-not-allowed.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-origin-not-allowed.html new file mode 100644 index 00000000..f2fbc8fa --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-origin-not-allowed.html @@ -0,0 +1,17 @@ + + + + + CORS Origin Violation + + +

CORS Origin Violation

+ + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-preflight-blocked.html b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-preflight-blocked.html new file mode 100644 index 00000000..247c4312 --- /dev/null +++ b/Api.PolicyHeaders.Cors/Api.PolicyHeaders.Cors/wwwroot/cors-violation-preflight-blocked.html @@ -0,0 +1,23 @@ + + + + + CORS Preflight Violation + + +

CORS Preflight Violation

+ + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/Dockerfile b/Api.PolicyHeaders.Cors/Dockerfile new file mode 100644 index 00000000..1c4803ef --- /dev/null +++ b/Api.PolicyHeaders.Cors/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Cors.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/LICENSE b/Api.PolicyHeaders.Cors/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.PolicyHeaders.Cors/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.Cors/README.md b/Api.PolicyHeaders.Cors/README.md new file mode 100644 index 00000000..fbff4206 --- /dev/null +++ b/Api.PolicyHeaders.Cors/README.md @@ -0,0 +1,54 @@ +# Api.PolicyHeaders.Cors + +> _Nano API application with cors._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +To test CORS behavior, open the provided HTML pages and observe how the browser enforces different CORS restrictions and blocks unauthorized requests. + +Also try out the endpoint, and observe how CORS returns the allowed hosts, headers, and methods. + +| Endpoint | Description | +| ----------------------------------------- | ----------------------------------------------------------------- | +| `http://localhost:8080/api/examples/cors` | Returns a `200 OK` response including the CORS response headers. | + +> 📖 Learn more about **[Nano Cors](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#cors)**. + +## Configuration +```json +"Cors": { + "AllowedOrigins": [ + "http://localhost:8080" + ], + "AllowedHeaders": [ + "Content-Type" + ], + "AllowedMethods": [ + "GET", + "POST", + "OPTIONS" + ], + "AllowCredentials": true, + "Origin": { + "EmbedderPolicy": "UnsafeNone", + "OpenerPolicy": "SameOriginAllowPopups", + "ResourcePolicy": "SameOrigin" + } +} +``` diff --git a/Api.PolicyHeaders.Cors/icon.png b/Api.PolicyHeaders.Cors/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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
+ + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.yml b/Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.yml new file mode 100644 index 00000000..c0253a40 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.policyheaders.forwardedheaders: + image: api.policyheaders.forwardedheaders + hostname: api-policyheaders-forwardedheaders + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.PolicyHeaders.ForwardedHeaders + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.dockerignore b/Api.PolicyHeaders.ForwardedHeaders/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.ForwardedHeaders/.github/config/slack.yml b/Api.PolicyHeaders.ForwardedHeaders/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..2d9f7da3 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.PolicyHeaders.ForwardedHeaders + IMAGE_NAME: api.policyheaders.forwardedheaders + SERVICE_NAME: api-policyheaders-forwardedheaders + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.gitignore b/Api.PolicyHeaders.ForwardedHeaders/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/service.yaml b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Tests.Api.PolicyHeaders.ForwardedHeaders.csproj b/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Tests.Api.PolicyHeaders.ForwardedHeaders.csproj new file mode 100644 index 00000000..13dbdb0e --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/.tests/Tests.Api.PolicyHeaders.ForwardedHeaders/Tests.Api.PolicyHeaders.ForwardedHeaders.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj new file mode 100644 index 00000000..890be2f1 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.Models/Api.PolicyHeaders.ForwardedHeaders.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.sln b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.sln new file mode 100644 index 00000000..b0532572 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ForwardedHeaders.Models", "Api.PolicyHeaders.ForwardedHeaders.Models\Api.PolicyHeaders.ForwardedHeaders.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ForwardedHeaders", "Api.PolicyHeaders.ForwardedHeaders\Api.PolicyHeaders.ForwardedHeaders.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.ForwardedHeaders", ".tests\Tests.Api.PolicyHeaders.ForwardedHeaders\Tests.Api.PolicyHeaders.ForwardedHeaders.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.csproj b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.csproj new file mode 100644 index 00000000..7f7bca61 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Controllers/ExamplesController.cs b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Controllers/ExamplesController.cs new file mode 100644 index 00000000..027ef17e --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Controllers/ExamplesController.cs @@ -0,0 +1,40 @@ +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.PolicyHeaders.ForwardedHeaders.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Forwarded Headers Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("forwarded-headers")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task ForwardedHeadersAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return Ok(new + { + RemoteIp = HttpContext.Connection.RemoteIpAddress?.ToString(), + HttpContext.Request.Scheme, + Host = HttpContext.Request.Host.Value, + OriginalHeaders = HttpContext.Request.Headers + .Where(x => x.Key.StartsWith("X-Original")) + .ToDictionary(x => x.Key, x => x.Value.ToString()) + }); + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local new file mode 100644 index 00000000..dcca3656 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ForwardedHeaders.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Program.cs b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..61df2b28 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.ForwardedHeaders")] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Development.json b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Production.json b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Staging.json b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.json b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.json new file mode 100644 index 00000000..e9f5b5fc --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Api.PolicyHeaders.ForwardedHeaders/appsettings.json @@ -0,0 +1,19 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HttpPolicyHeaders": { + "ForwardedHeaders": { + "Headers": "All", + "RequireHeaderSymmetry": true + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/Dockerfile b/Api.PolicyHeaders.ForwardedHeaders/Dockerfile new file mode 100644 index 00000000..2efeaed7 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ForwardedHeaders.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/LICENSE b/Api.PolicyHeaders.ForwardedHeaders/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.ForwardedHeaders/README.md b/Api.PolicyHeaders.ForwardedHeaders/README.md new file mode 100644 index 00000000..6aaf0b8c --- /dev/null +++ b/Api.PolicyHeaders.ForwardedHeaders/README.md @@ -0,0 +1,42 @@ +# Api.PolicyHeaders.ForwardedHeaders + +> _Nano API application with forwarded headers enabled._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +Invoke the endpoint in the example controller to see the updated scheme, host, and remote IP address. The response also includes the original headers, +which reflect the internal service values rather than the forwarded ones. The example request includes three `X-Forwarded-*` headers to simulate a reverse proxy scenario. + +| Endpoint | Description | +| -------------------------------------------- | ------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/nosniff` | Returns a `200 OK` response with `HttpContext` forwarded header values. | + +> 📖 Learn more about **[Nano Forwarded Headers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#forwarded-headers)**. + +## Configuration +```json +"App": { + "HttpPolicyHeaders": { + "ForwardedHeaders": { + "Headers": "All", + "RequireHeaderSymmetry": true + } + } +} +``` diff --git a/Api.PolicyHeaders.ForwardedHeaders/icon.png b/Api.PolicyHeaders.ForwardedHeaders/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.docker/docker-compose.yml b/Api.PolicyHeaders.FrameOptions/.docker/docker-compose.yml new file mode 100644 index 00000000..641fe30c --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.policyheaders.frameoptions: + image: api.policyheaders.frameoptions + hostname: api-policyheaders-frameoptions + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.PolicyHeaders.FrameOptions + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.dockerignore b/Api.PolicyHeaders.FrameOptions/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.FrameOptions/.github/config/slack.yml b/Api.PolicyHeaders.FrameOptions/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..504d5a7e --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.PolicyHeaders.FrameOptions + IMAGE_NAME: api.policyheaders.frameoptions + SERVICE_NAME: api-policyheaders-frameoptions + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.gitignore b/Api.PolicyHeaders.FrameOptions/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.PolicyHeaders.FrameOptions/.kubernetes/service.yaml b/Api.PolicyHeaders.FrameOptions/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Tests.Api.PolicyHeaders.FrameOptions.csproj b/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Tests.Api.PolicyHeaders.FrameOptions.csproj new file mode 100644 index 00000000..060b06ae --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/.tests/Tests.Api.PolicyHeaders.ContentType/Tests.Api.PolicyHeaders.FrameOptions.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj new file mode 100644 index 00000000..890be2f1 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.Models/Api.PolicyHeaders.FrameOptions.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.sln b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.sln new file mode 100644 index 00000000..e7325555 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.FrameOptions.Models", "Api.PolicyHeaders.FrameOptions.Models\Api.PolicyHeaders.FrameOptions.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.FrameOptions", "Api.PolicyHeaders.FrameOptions\Api.PolicyHeaders.FrameOptions.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.FrameOptions", ".tests\Tests.Api.PolicyHeaders.FrameOptions\Tests.Api.PolicyHeaders.FrameOptions.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.csproj b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.csproj new file mode 100644 index 00000000..f9a4cf22 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Controllers/ExamplesController.cs b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Controllers/ExamplesController.cs new file mode 100644 index 00000000..10a7c1ab --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.PolicyHeaders.FrameOptions.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Nosniff Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("frameoptions")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task FrameOptionsAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("frameoptions"); + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local new file mode 100644 index 00000000..bac6d4bb --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.FrameOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Program.cs b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..7edcb0e2 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.FrameOptions")] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Development.json b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Production.json b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Staging.json b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.json b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.json new file mode 100644 index 00000000..5d172fb4 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/appsettings.json @@ -0,0 +1,18 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HttpPolicyHeaders": { + "FrameOptions": { + "FrameOptionsPolicyHeader": "SameOrigin" + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/wwwroot/frame-options-violation.html b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/wwwroot/frame-options-violation.html new file mode 100644 index 00000000..3e3a7e2d --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Api.PolicyHeaders.FrameOptions/wwwroot/frame-options-violation.html @@ -0,0 +1,27 @@ + + + + + X-Frame-Options SAMEORIGIN Violation + + + +

X-Frame-Options: SAMEORIGIN Test

+ +

This page attempts to embed a page protected with X-Frame-Options: SAMEORIGIN from a different origin.

+ + + +

Expected result: the iframe is blocked and remains blank. Check the browser console for a violation message.

+ + \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/Dockerfile b/Api.PolicyHeaders.FrameOptions/Dockerfile new file mode 100644 index 00000000..dae0c7d9 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.FrameOptions.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/LICENSE b/Api.PolicyHeaders.FrameOptions/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/README.md b/Api.PolicyHeaders.FrameOptions/README.md new file mode 100644 index 00000000..af5298c6 --- /dev/null +++ b/Api.PolicyHeaders.FrameOptions/README.md @@ -0,0 +1,40 @@ +# Api.PolicyHeaders.FrameOptions + +> _Nano API application with frame options._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +To observe X-Frame-Options enforcement, load the `frame-options-violation.html` file and see the browser block the page from being embedded in an iframe. + +| Endpoint | Description | +| ------------------------------------------------- | ----------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/frameoptions` | Returns a `200 OK` response including the `X-Frame-Options` response header. | + +> 📖 Learn more about **[Nano Frame Options Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#frame-options)**. + +## Configuration +```json +"App": { + "HttpPolicyHeaders": { + "FrameOptions": { + "FrameOptionsPolicyHeader": "SameOrigin" + } + } +} +``` \ No newline at end of file diff --git a/Api.PolicyHeaders.FrameOptions/icon.png b/Api.PolicyHeaders.FrameOptions/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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
+ + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.docker/docker-compose.yml b/Api.PolicyHeaders.Hsts/.docker/docker-compose.yml new file mode 100644 index 00000000..0c46f0dd --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.docker/docker-compose.yml @@ -0,0 +1,20 @@ +services: + api.policyheaders.hsts: + image: api.policyheaders.hsts + hostname: api-policyheaders-hsts + restart: on-failure + ports: + - 8080:8080 + - 4443:4443 + build: + context: ../Api.PolicyHeaders.Hsts + dockerfile: "Dockerfile.Local" + volumes: + - ../:/root/.dotnet/https + networks: + - network + +networks: + network: + name: network + driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.dockerignore b/Api.PolicyHeaders.Hsts/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.Hsts/.github/config/slack.yml b/Api.PolicyHeaders.Hsts/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..2a01df3e --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.github/workflows/build-and-deploy.yml @@ -0,0 +1,205 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.PolicyHeaders.Hsts + IMAGE_NAME: api.policyheaders.hsts + SERVICE_NAME: api-policyheaders-hsts + SUB_DOMAIN_NAME: nano + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_DNS: ${{ vars.AZURE_RESOURCE_GROUP_DNS }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $zoneNames = az network dns zone list -g $env:AZURE_GROUP_DNS --query "[].name" -o json | ConvertFrom-Json + + $env:ROUTE_HOST_NAMES = ( + $zoneNames | ForEach-Object { + " - $env:SUB_DOMAIN_NAME.$_" + } + ) -join "`n" + + $env:GATEWAY_NAME = kubectl get gateway -n apps -o jsonpath='{.items[0].metadata.name}' + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/grafana-httproute-80.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-80.tmp.yaml; + kubectl apply -f .kubernetes/grafana-httproute-80.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + Get-Content .kubernetes/grafana-httproute-443.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/grafana-httproute-443.tmp.yaml; + kubectl apply -f .kubernetes/grafana-httproute-443.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + } + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.gitignore b/Api.PolicyHeaders.Hsts/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/httproute-443.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/httproute-443.yaml new file mode 100644 index 00000000..65b97a51 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.kubernetes/httproute-443.yaml @@ -0,0 +1,18 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %SERVICE_NAME%-route + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + hostnames: +%ROUTE_HOST_NAMES% + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: %SERVICE_NAME% + port: 8080 \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/httproute-80.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/httproute-80.yaml new file mode 100644 index 00000000..f29775aa --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.kubernetes/httproute-80.yaml @@ -0,0 +1,17 @@ +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: %APP_NAME%-route-80 + namespace: %KUBERNETES_NAMESPACE% +spec: + parentRefs: + - name: %GATEWAY_NAME% + sectionName: http + hostnames: +%ROUTE_HOST_NAMES% + rules: + - filters: + - type: RequestRedirect + requestRedirect: + scheme: https + statusCode: 301 \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.kubernetes/service.yaml b/Api.PolicyHeaders.Hsts/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Tests.Api.PolicyHeaders.Hsts.csproj b/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Tests.Api.PolicyHeaders.Hsts.csproj new file mode 100644 index 00000000..51e93680 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/.tests/Tests.Api.PolicyHeaders.Hsts/Tests.Api.PolicyHeaders.Hsts.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj new file mode 100644 index 00000000..890be2f1 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.Models/Api.PolicyHeaders.Hsts.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.sln b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.sln new file mode 100644 index 00000000..c3c334db --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.sln @@ -0,0 +1,137 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + localhost.pfx = localhost.pfx + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\httproute-443.yaml = .kubernetes\httproute-443.yaml + .kubernetes\httproute-80.yaml = .kubernetes\httproute-80.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Hsts.Models", "Api.PolicyHeaders.Hsts.Models\Api.PolicyHeaders.Hsts.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Hsts", "Api.PolicyHeaders.Hsts\Api.PolicyHeaders.Hsts.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.Hsts", ".tests\Tests.Api.PolicyHeaders.Hsts\Tests.Api.PolicyHeaders.Hsts.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.csproj b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.csproj new file mode 100644 index 00000000..7db249e4 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Controllers/ExamplesController.cs b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Controllers/ExamplesController.cs new file mode 100644 index 00000000..3613c10d --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.PolicyHeaders.Hsts.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Hsts Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("hsts")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task HstsAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("hsts"); + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local new file mode 100644 index 00000000..86fa8383 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 4443 + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Hsts.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Program.cs b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..671ab2b3 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.Hsts")] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Development.json b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Development.json new file mode 100644 index 00000000..8da7c4bf --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Development.json @@ -0,0 +1,15 @@ +{ + "App": { + "Hosting": { + "Https": { + "Ports": [ + 4443 + ], + "Certificate": { + "Path": "/root/.dotnet/https/localhost.pfx", + "Password": "password" + } + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Production.json b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Staging.json b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.json b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.json new file mode 100644 index 00000000..1f00e26e --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/appsettings.json @@ -0,0 +1,20 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HttpPolicyHeaders": { + "Hsts": { + "MaxAge": "00:01:00", + "UsePreload": false, + "IncludeSubdomains": true + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/wwwroot/hsts-violation-image.png b/Api.PolicyHeaders.Hsts/Api.PolicyHeaders.Hsts/wwwroot/hsts-violation-image.png new file mode 100644 index 0000000000000000000000000000000000000000..1d79719a50bcfb9e5373641c1b98613c12b0b03a GIT binary patch literal 15262 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 + + + + HSTS Test on Localhost + + +

HSTS Test on Localhost

+ +

This page demonstrates HTTP Strict Transport Security (HSTS) behavior. HSTS instructs browsers to always use HTTPS for a given host.

+ +

+ Important notes for localhost: +

    +
  • Browsers normally exclude localhost from automatic HSTS upgrades to avoid dev lock-in.
  • +
  • Because we cannot bind HTTP to the same port as HTTPS, we cannot fully test automatic HTTP → HTTPS upgrade on localhost.
  • +
  • We can still verify that the Strict-Transport-Security header is being sent and cached by the browser.
  • +
+

+ +

+ Observed behavior in this page: +

    +
  • All resources use HTTPS to satisfy HSTS.
  • +
  • Direct HTTP requests on other ports (e.g., port 8080) will not be upgraded, because HSTS is port-specific.
  • +
  • Mixed-content blocking is enforced normally by the browser.
  • +
+

+ + +
+ + + + + + HSTS test image + +

Note: Automatic upgrade from HTTP → HTTPS cannot be demonstrated on localhost because the HTTP port differs from the HTTPS port. This behavior works on real hosts in production.

+ + \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/Dockerfile b/Api.PolicyHeaders.Hsts/Dockerfile new file mode 100644 index 00000000..477de8f6 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Hsts.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/LICENSE b/Api.PolicyHeaders.Hsts/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.Hsts/README.md b/Api.PolicyHeaders.Hsts/README.md new file mode 100644 index 00000000..0863fd81 --- /dev/null +++ b/Api.PolicyHeaders.Hsts/README.md @@ -0,0 +1,43 @@ +# Api.PolicyHeaders.Hsts + +> _Nano API application with strict-transport-security (HSTS)._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Hosting.Https](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api.Hosting.Https)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +The service is configured to run over HTTPS, since HSTS requires a secure connection to be tested effectively. +To observe HSTS enforcement in action, load the `hsts-violation.html` file and see how the browser blocks non-secure requests. + +| Endpoint | Description | +| ----------------------------------------- | --------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/hsts` | Returns a `200 OK` response including the `Strict-Transform-Security` response header. | + +> 📖 Learn more about **[Nano Strict Transport Security (HSTS)](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#strict-transport-security-hsts)**. + +## Configuration +```json +"App": { + "HttpPolicyHeaders": { + "Hsts": { + "MaxAge": "00:01:00", + "UsePreload": false, + "IncludeSubdomains": true + } + } +} +``` diff --git a/Api.PolicyHeaders.Hsts/icon.png b/Api.PolicyHeaders.Hsts/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 QtUG7zSmhZ)eg(jP05{mZ`~Uy| literal 0 HcmV?d00001 diff --git a/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.dcproj b/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.dcproj new file mode 100644 index 00000000..d9e8e500 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.dcproj @@ -0,0 +1,13 @@ + + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.yml b/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.yml new file mode 100644 index 00000000..a3364eab --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.policyheaders.referrerpolicy: + image: api.policyheaders.referrerpolicy + hostname: api-policyheaders-referrerpolicy + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.PolicyHeaders.ReferrerPolicy + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.dockerignore b/Api.PolicyHeaders.ReferrerPolicy/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.PolicyHeaders.ReferrerPolicy/.github/config/slack.yml b/Api.PolicyHeaders.ReferrerPolicy/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..9ae23278 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.PolicyHeaders.ReferrerPolicy + IMAGE_NAME: api.policyheaders.referrerpolicy + SERVICE_NAME: api-policyheaders-referrerpolicy + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.gitignore b/Api.PolicyHeaders.ReferrerPolicy/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/service.yaml b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Tests.Api.PolicyHeaders.ReferrerPolicy.csproj b/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Tests.Api.PolicyHeaders.ReferrerPolicy.csproj new file mode 100644 index 00000000..a8eb9bd7 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/.tests/Tests.Api.PolicyHeaders.ReferrerPolicy/Tests.Api.PolicyHeaders.ReferrerPolicy.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj new file mode 100644 index 00000000..890be2f1 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.Models/Api.PolicyHeaders.ReferrerPolicy.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.sln b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.sln new file mode 100644 index 00000000..1f1a70d8 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.sln @@ -0,0 +1,134 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ReferrerPolicy.Models", "Api.PolicyHeaders.ReferrerPolicy.Models\Api.PolicyHeaders.ReferrerPolicy.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.ReferrerPolicy", "Api.PolicyHeaders.ReferrerPolicy\Api.PolicyHeaders.ReferrerPolicy.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.ReferrerPolicy", ".tests\Tests.Api.PolicyHeaders.ReferrerPolicy\Tests.Api.PolicyHeaders.ReferrerPolicy.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.csproj b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.csproj new file mode 100644 index 00000000..b8c792a6 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Controllers/ExamplesController.cs b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Controllers/ExamplesController.cs new file mode 100644 index 00000000..82f02d8b --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.PolicyHeaders.ReferrerPolicy.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Referrer Policy Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("referrer-policy")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task ReferrerPolicyAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("referrer-policy"); + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local new file mode 100644 index 00000000..fc184f16 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ReferrerPolicy.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Program.cs b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..9e08687b --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.ReferrerPolicy")] \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Development.json b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Production.json b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Staging.json b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.json b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.json new file mode 100644 index 00000000..6c782770 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/appsettings.json @@ -0,0 +1,18 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HttpPolicyHeaders": { + "ReferrerPolicy": { + "ReferrerPolicyHeader": "SameOrigin" + } + } + } +} \ No newline at end of file diff --git a/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/wwwroot/referrer-policy-violation.html b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/wwwroot/referrer-policy-violation.html new file mode 100644 index 00000000..e9460934 --- /dev/null +++ b/Api.PolicyHeaders.ReferrerPolicy/Api.PolicyHeaders.ReferrerPolicy/wwwroot/referrer-policy-violation.html @@ -0,0 +1,55 @@ + + + + + Referrer Policy Test + + +

Referrer Policy Test: same-origin

+ +

+ This page tests Referrer-Policy: same-origin. +

    +
  • Links or requests to the same origin will include the Referer header.
  • +
  • Links or requests to a different origin will not include the Referer.
  • +
+

+ +

1. Same-origin link

+

+ Click here (same origin) +

+ +

2. Cross-origin link

+

+ Click here (different origin) +

+ +

3. Same-origin fetch

+ +

+
+

4. Cross-origin fetch

+ +

+
+
+
+
\ No newline at end of file
diff --git a/Api.PolicyHeaders.ReferrerPolicy/Dockerfile b/Api.PolicyHeaders.ReferrerPolicy/Dockerfile
new file mode 100644
index 00000000..48e82726
--- /dev/null
+++ b/Api.PolicyHeaders.ReferrerPolicy/Dockerfile
@@ -0,0 +1,35 @@
+ARG DOTNET_SDK_VERSION
+ARG DOTNET_ASPNET_VERSION
+ARG CONTAINER_REGISTRY_SOURCE_LABEL
+ARG NUGET_HOST
+ARG NUGET_USERNAME
+ARG NUGET_PASSWORD
+
+FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
+
+LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL
+
+EXPOSE 8080
+WORKDIR /app
+
+FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build
+ARG NUGET_HOST
+ARG NUGET_USERNAME
+ARG NUGET_PASSWORD
+
+WORKDIR /src
+COPY . .
+
+RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text
+RUN dotnet build -c Release -o /app
+
+FROM build AS publish
+RUN dotnet publish -c Release -o /app
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app .
+ENV COMPlus_EnableDiagnostics=0
+ENV DOTNET_USE_POLLING_FILE_WATCHER=true
+
+ENTRYPOINT ["dotnet", "Api.PolicyHeaders.ReferrerPolicy.dll"]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.ReferrerPolicy/LICENSE b/Api.PolicyHeaders.ReferrerPolicy/LICENSE
new file mode 100644
index 00000000..006e8c21
--- /dev/null
+++ b/Api.PolicyHeaders.ReferrerPolicy/LICENSE
@@ -0,0 +1,18 @@
+The MIT License (MIT)
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/Api.PolicyHeaders.ReferrerPolicy/README.md b/Api.PolicyHeaders.ReferrerPolicy/README.md
new file mode 100644
index 00000000..2418a357
--- /dev/null
+++ b/Api.PolicyHeaders.ReferrerPolicy/README.md
@@ -0,0 +1,41 @@
+# Api.PolicyHeaders.ReferrerPolicy
+
+> _Nano API application with referrer policy._  
+_All lessons are complete, self-contained examples that include build and deployment setup._
+
+> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. 
+Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._
+
+> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio.
+
+> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**.
+
+***
+
+## Table of Contents
+* [Summary](#summary)
+* [Configuration](#configuration)
+
+## Summary
+This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller 
+that inherits from the top-level Nano `BaseController`.  
+
+To observe referrer policy behavior in action, load the `referrer-policy-violation.html` file and inspect how the browser handles (or blocks) the referrer 
+in the resulting request.  
+
+| Endpoint                                             | Description                                                                                        |
+| ---------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
+| `http://localhost:8080/api/examples/referrer-policy` | Returns a `200 OK` response including the `Referrer-Policy` response header set to `same-origin`.  |
+
+> 📖 Learn more about **[Nano Referrer Policy Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#referrer-policy)**.
+
+## Configuration
+```json
+"App": {
+  "HttpPolicyHeaders": {
+    "ReferrerPolicy": {
+      "ReferrerPolicyHeader": "SameOrigin"
+    }
+  }
+}
+```
diff --git a/Api.PolicyHeaders.ReferrerPolicy/icon.png b/Api.PolicyHeaders.ReferrerPolicy/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5
GIT binary patch
literal 14103
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
+
+  
+    2.1
+    Linux
+    false
+    http://localhost:{ServicePort}/docs
+    $(ProjectName)
+  
+  
+    
+  
+
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.docker/docker-compose.yml b/Api.PolicyHeaders.Robots/.docker/docker-compose.yml
new file mode 100644
index 00000000..b45799e9
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.docker/docker-compose.yml
@@ -0,0 +1,17 @@
+services:
+  api.policyheaders.robots:
+    image: api.policyheaders.robots
+    hostname: api-policyheaders-robots
+    restart: on-failure
+    ports:
+      - 8080:8080
+    build:
+      context: ../Api.PolicyHeaders.Robots
+      dockerfile: "Dockerfile.Local"
+    networks:
+      - network
+
+networks:
+  network:
+    name: network
+    driver: bridge
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.dockerignore b/Api.PolicyHeaders.Robots/.dockerignore
new file mode 100644
index 00000000..e694ae21
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.dockerignore
@@ -0,0 +1,12 @@
+.dockerignore
+.env
+.git
+.gitignore
+.vs
+.vscode
+docker-compose.yml
+docker-compose.*.yml
+*/bin
+*/obj
+!obj/Docker/publish/*
+!obj/Docker/empty/
diff --git a/Api.PolicyHeaders.Robots/.github/config/slack.yml b/Api.PolicyHeaders.Robots/.github/config/slack.yml
new file mode 100644
index 00000000..3592affb
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.github/config/slack.yml
@@ -0,0 +1,18 @@
+username: GitHub Actions
+icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png
+
+pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>."
+
+text: |
+  {{#if payload.commits}}
+  *Commits*
+  {{#each payload.commits}}
+  <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}}
+  {{/each}}
+  {{/if}}
+
+footer: >-
+  <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}}
+  
+fallback: |-
+  [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}}
diff --git a/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml
new file mode 100644
index 00000000..3699c699
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.github/workflows/build-and-deploy.yml
@@ -0,0 +1,179 @@
+name: Build And Deploy
+on:
+  pull_request:
+    branches:
+      - master 
+  push:
+    branches:
+      - master 
+env:
+  APP_NAME: Api.PolicyHeaders.Robots
+  IMAGE_NAME: api.policyheaders.robots
+  SERVICE_NAME: api-policyheaders-robots
+  VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}'
+  DOTNET_SDK_VERSION: 10.0
+  DOTNET_ASPNET_VERSION: 10.0
+  AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }}
+  AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
+  AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
+  AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
+  AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }}
+  NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
+  NUGET_USERNAME: ${{ github.actor }}
+  NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+  CONTAINER_REGISTRY_HOST: ghcr.io
+  CONTAINER_REGISTRY_USERNAME: ${{ github.actor }}
+  CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+  CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }}
+  KUBERNETES_NODEPOOL_COMPUTE: cpu
+  KUBERNETES_NAMESPACE: apps
+  KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }}
+  KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }}
+  KUBERNETES_REPLICA_HISTORY_COUNT: 0
+  KUBERNETES_MEMORY_REQUEST: 512Mi   
+  KUBERNETES_MEMORY_LIMIT: 1536Mi
+  KUBERNETES_MEMORY_SCALING: 180
+  KUBERNETES_CPU_REQUEST: 200m
+  KUBERNETES_CPU_LIMIT: 600m
+  KUBERNETES_CPU_SCALING: 180
+  ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }}
+jobs:
+  build-and-deploy:
+    runs-on:
+      - self-hosted
+      - linux 
+      - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }}
+    permissions:
+      contents: write
+      packages: write
+      id-token: write
+    concurrency:
+      group: ${{ github.repository }}
+      cancel-in-progress: true
+    steps:
+      - uses: actions/checkout@v6
+
+      - name: Azure Login
+        shell: pwsh
+        run: |
+          az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none;
+          az account set -s $env:AZURE_SUBSCRIPTION_ID -o none;
+
+          $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv;
+          az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none;
+
+      - name: Build
+        shell: pwsh
+        run: |
+          dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text;
+
+          dotnet build -c Release .\$env:APP_NAME.sln;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+      - name: Test
+        shell: pwsh
+        run: |
+          dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj;
+          if ($LastExitCode -ne 0)
+          { 
+            throw "error";
+          };
+
+      - name: Publish Image
+        shell: pwsh
+        run: |
+          $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower()
+          $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest";
+          $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION
+          
+          docker build `
+              -t $imageLatestTag `
+              -t $imageVersionTag `
+              --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION `
+              --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION `
+              --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL `
+              --build-arg NUGET_HOST=$env:NUGET_HOST `
+              --build-arg NUGET_USERNAME=$env:NUGET_USERNAME `
+              --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD `
+              ./;
+
+          if ($LastExitCode -ne 0) 
+          { 
+              throw "error";
+          };
+
+          echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin;
+          docker push $imageLatestTag;
+          docker push $imageVersionTag;
+          if ($LastExitCode -ne 0) 
+          { 
+              throw "error";
+          };  
+
+      - name: Publish NuGet
+        shell: pwsh
+        run: |
+          $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj";
+          dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build;
+          dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD;
+          if ($LastExitCode -ne 0) 
+          { 
+              throw "error";
+          };  
+
+      - name: Kubernetes Deploy
+        shell: pwsh
+        run: |
+          Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml;
+          kubectl apply -f .kubernetes/service.tmp.yaml;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+          Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml;
+          kubectl apply -f .kubernetes/configmap.tmp.yaml;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+          Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml;
+          kubectl apply -f .kubernetes/deployment.tmp.yaml;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+          Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml;
+          kubectl apply -f .kubernetes/autoscaler.tmp.yaml;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+      - name: GitHub Release
+        if: github.ref == 'refs/heads/master'
+        uses: ncipollo/release-action@v1
+        with:
+          tag: v${{ env.VERSION }}
+          name: "Release ${{ env.VERSION }}"
+          body: |
+            Version: ${{ env.VERSION }}
+            Commit: ${{ github.sha }}
+          artifacts: "nupkgs/*"
+          token: ${{ secrets.GITHUB_TOKEN }}
+          draft: false
+          prerelease: false
+
+      - name: Slack Notification
+        if: always()
+        uses: act10ns/slack@v2
+        with:
+          webhook-url: ${{ secrets.SLACK_WEBHOOK }}
+          config: .github/config/slack.yml
+          status: ${{ job.status }}
+          channel: ${{ vars.SLACK_CHANNEL }}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.gitignore b/Api.PolicyHeaders.Robots/.gitignore
new file mode 100644
index 00000000..9921fc06
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.gitignore
@@ -0,0 +1,11 @@
+.vs
+*.user
+*.userprefs
+*.suo
+_ReSharper*
+**/bin
+**/obj
+*.DotSettings.User
+packages
+.env
+**/Properties/launchSettings.json
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.Robots/.kubernetes/autoscaler.yaml
new file mode 100644
index 00000000..95add8ad
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.kubernetes/autoscaler.yaml
@@ -0,0 +1,25 @@
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+  name: %SERVICE_NAME%-hpa
+  namespace: %KUBERNETES_NAMESPACE%
+spec:
+  minReplicas: %KUBERNETES_REPLICA_COUNT%
+  maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX%
+  scaleTargetRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: %SERVICE_NAME%
+  metrics:
+    - type: Resource
+      resource:
+        name: cpu
+        target:
+          type: Utilization
+          averageUtilization: %KUBERNETES_CPU_SCALING%
+    - type: Resource
+      resource:
+        name: memory
+        target:
+          type: Utilization
+          averageUtilization: %KUBERNETES_MEMORY_SCALING%
diff --git a/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml b/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml
new file mode 100644
index 00000000..3977c0ee
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.kubernetes/configmap.yaml
@@ -0,0 +1,8 @@
+  apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: %SERVICE_NAME%-config
+  namespace: %KUBERNETES_NAMESPACE%
+data:
+  App__Version: %VERSION%
+  ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT%
diff --git a/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml b/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml
new file mode 100644
index 00000000..02882c56
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.kubernetes/deployment.yaml
@@ -0,0 +1,78 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: %SERVICE_NAME%
+  namespace: %KUBERNETES_NAMESPACE%
+  labels:
+    app: %SERVICE_NAME%
+spec:
+  replicas: %KUBERNETES_REPLICA_COUNT%
+  revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT%
+  selector:
+    matchLabels:
+      app: %SERVICE_NAME%
+  template:
+    metadata:
+      labels:
+        app: %SERVICE_NAME%
+    spec:
+      automountServiceAccountToken: false
+      securityContext:
+        runAsUser: 1000
+        runAsGroup: 2000 
+        fsGroup: 2000
+      topologySpreadConstraints:
+        - maxSkew: 1
+          topologyKey: kubernetes.io/hostname
+          whenUnsatisfiable: ScheduleAnyway
+          labelSelector:
+            matchLabels:
+              app: %SERVICE_NAME%  
+      nodeSelector:
+        nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE%
+        kubernetes.io/os: linux
+      containers:
+      - name: %SERVICE_NAME%
+        image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION%
+        ports:
+        - containerPort: 8080
+        imagePullPolicy: Always 
+        envFrom:
+        - configMapRef:
+            name: %SERVICE_NAME%-config
+        resources:
+          requests:
+            memory: %KUBERNETES_MEMORY_REQUEST%
+            cpu: %KUBERNETES_CPU_REQUEST%
+          limits:
+            memory: %KUBERNETES_MEMORY_LIMIT%
+            cpu: %KUBERNETES_CPU_LIMIT%
+        securityContext:
+          privileged: false
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          runAsNonRoot: true
+          runAsUser: 1000
+          runAsGroup: 2000
+          capabilities:
+            drop:
+            - ALL
+        livenessProbe:
+          httpGet:
+            path: /healthz
+            port: 8080
+            scheme: HTTP
+          periodSeconds: 10
+          initialDelaySeconds: 30
+          timeoutSeconds: 2
+        readinessProbe:
+          httpGet:
+            path: /healthz
+            port: 8080
+            scheme: HTTP
+          periodSeconds: 5
+          initialDelaySeconds: 20
+          timeoutSeconds: 2
+      imagePullSecrets:
+        - name: ghcr-pull-secret
+
diff --git a/Api.PolicyHeaders.Robots/.kubernetes/service.yaml b/Api.PolicyHeaders.Robots/.kubernetes/service.yaml
new file mode 100644
index 00000000..2d8e20b2
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.kubernetes/service.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: %SERVICE_NAME%
+  namespace: %KUBERNETES_NAMESPACE%
+spec:
+  ports:  
+  - name: http
+    port: 8080
+  selector:
+    app: %SERVICE_NAME%
+  type: ClusterIP
diff --git a/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Properties/DoNotParallelize.cs
new file mode 100644
index 00000000..6a076669
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Properties/DoNotParallelize.cs
@@ -0,0 +1,3 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+[assembly: DoNotParallelize]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Tests.Api.PolicyHeaders.Robots.csproj b/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Tests.Api.PolicyHeaders.Robots.csproj
new file mode 100644
index 00000000..6a6d6098
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/.tests/Tests.Api.PolicyHeaders.Robots/Tests.Api.PolicyHeaders.Robots.csproj
@@ -0,0 +1,23 @@
+
+
+	
+		net10.0
+		false
+		latest
+		
+		
+		true
+	
+
+	
+		
+		
+		
+	
+
+	
+	  
+	  
+	
+
+
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj
new file mode 100644
index 00000000..890be2f1
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.Models/Api.PolicyHeaders.Robots.Models.csproj
@@ -0,0 +1,75 @@
+
+
+	
+		net10.0
+		
+		10.0.0.0
+		10.0.0.0
+		10.0.0.0
+		AnyCPU
+		Debug;Release
+		latest
+		en-US
+		enable
+		LICENSE
+		true
+		
+		True
+		true
+		true
+		true
+		Michael Vivet
+		Michael Vivet
+		$(ProjectName)
+		Example project demonstrating a focused aspect of the Nano Library.
+		
+			A practical example highlighting a specific area or scenario of Nano for learning
+			and experimentation. Shows how features or patterns can be applied without
+			representing a full application or complete reference.
+		
+		
+			- Added .NET 10 support
+		
+		git
+		master
+		https://github.com/Nano-Core/Nano.Examples.git
+		$(GitCommitHash)
+		true
+		true
+		true
+		snupkg
+		true
+		$(Version)
+		nano;example;sample;framework;library;learning;showcase
+		https://github.com/Nano-Core/Nano.Library/$(ProjectName)
+		LICENSE
+		icon.png
+		README.md
+		true
+	
+
+	
+		True
+	
+
+	
+		
+		
+		
+	
+
+	
+		
+	
+
+	
+	  
+	  
+	  
+	  
+	  
+	  
+	  
+	
+
+
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.sln b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.sln
new file mode 100644
index 00000000..39697721
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.sln
@@ -0,0 +1,134 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 18
+VisualStudioVersion = 18.1.11312.151
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}"
+	ProjectSection(SolutionItems) = preProject
+		.dockerignore = .dockerignore
+		.gitignore = .gitignore
+		Dockerfile = Dockerfile
+		icon.png = icon.png
+		LICENSE = LICENSE
+		README.md = README.md
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}"
+	ProjectSection(SolutionItems) = preProject
+		.kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml
+		.kubernetes\configmap.yaml = .kubernetes\configmap.yaml
+		.kubernetes\deployment.yaml = .kubernetes\deployment.yaml
+		.kubernetes\service.yaml = .kubernetes\service.yaml
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}"
+	ProjectSection(SolutionItems) = preProject
+		.github\config\slack.yml = .github\config\slack.yml
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}"
+	ProjectSection(SolutionItems) = preProject
+		.github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml
+	EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Robots.Models", "Api.PolicyHeaders.Robots.Models\Api.PolicyHeaders.Robots.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.Robots", "Api.PolicyHeaders.Robots\Api.PolicyHeaders.Robots.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.Robots", ".tests\Tests.Api.PolicyHeaders.Robots\Tests.Api.PolicyHeaders.Robots.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}"
+EndProject
+Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU
+		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU
+		{345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A}
+		{F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A}
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE}
+	EndGlobalSection
+EndGlobal
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.csproj b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.csproj
new file mode 100644
index 00000000..15b582a6
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots.csproj
@@ -0,0 +1,37 @@
+
+
+	
+		net10.0
+		
+		10.0.0.0
+		10.0.0.0
+		10.0.0.0
+		AnyCPU
+		Debug;Release
+		latest
+		en-US
+		enable
+		LICENSE
+		true
+		
+		false
+		true
+		false
+		true
+		Linux
+		..\docker-compose.dcproj
+	
+
+	
+		True
+	
+
+	
+		
+	
+
+	
+	    
+	
+
+
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Controllers/ExamplesController.cs b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Controllers/ExamplesController.cs
new file mode 100644
index 00000000..47df563c
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Controllers/ExamplesController.cs
@@ -0,0 +1,31 @@
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using Nano.App.Api.Controllers;
+
+namespace Api.PolicyHeaders.Robots.Controllers;
+
+/// 
+/// Controller with examples.
+/// 
+/// The .
+public class ExamplesController(ILogger logger) : BaseController(logger)
+{
+    /// 
+    /// Robots Action.
+    /// 
+    /// The cancellation token.
+    /// A message.
+    /// Success.
+    [HttpGet]
+    [Route("robots")]
+    [ProducesResponseType((int)HttpStatusCode.OK)]
+    public virtual async Task RobotsAsync(CancellationToken cancellationToken = default)
+    {
+        await Task.CompletedTask;
+
+        return this.Ok("robots");
+    }
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local
new file mode 100644
index 00000000..8a943b01
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Dockerfile.Local
@@ -0,0 +1,6 @@
+ARG DOTNET_ASPNET_VERSION="10.0"
+FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
+
+EXPOSE 8080
+
+ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Robots.dll"]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Program.cs b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Program.cs
new file mode 100644
index 00000000..32865375
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Program.cs
@@ -0,0 +1,10 @@
+using Nano.App.Api;
+
+NanoApiApplication
+    .ConfigureApp()
+    .ConfigureServices(_ =>
+    {
+        // Blank
+    })
+    .Build()
+    .Run();
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Properties/InternalsVisibleTo.cs
new file mode 100644
index 00000000..3b8eb8c2
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/Properties/InternalsVisibleTo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.Robots")]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Development.json b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Development.json
new file mode 100644
index 00000000..8593c62d
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Development.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Production.json b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Production.json
new file mode 100644
index 00000000..8593c62d
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Production.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Staging.json b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Staging.json
new file mode 100644
index 00000000..8593c62d
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.Staging.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.json b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.json
new file mode 100644
index 00000000..50830b74
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Api.PolicyHeaders.Robots/appsettings.json
@@ -0,0 +1,24 @@
+{
+  "App": {
+    "Version": "1.0.0.0",
+    "Hosting": {
+      "Root": "api",
+      "Http": {
+        "Ports": [
+          8080
+        ]
+      }
+    },
+    "HttpPolicyHeaders": {
+      "Robots": {
+        "UseNoIndex": true,
+        "UseNoFollow": true,
+        "UseNoSnippet": true,
+        "UseNoArchive": true,
+        "UseNoOdp": true,
+        "UseNoTranslate": true,
+        "UseNoImageIndex": true
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/Dockerfile b/Api.PolicyHeaders.Robots/Dockerfile
new file mode 100644
index 00000000..f349f074
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/Dockerfile
@@ -0,0 +1,35 @@
+ARG DOTNET_SDK_VERSION
+ARG DOTNET_ASPNET_VERSION
+ARG CONTAINER_REGISTRY_SOURCE_LABEL
+ARG NUGET_HOST
+ARG NUGET_USERNAME
+ARG NUGET_PASSWORD
+
+FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
+
+LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL
+
+EXPOSE 8080
+WORKDIR /app
+
+FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build
+ARG NUGET_HOST
+ARG NUGET_USERNAME
+ARG NUGET_PASSWORD
+
+WORKDIR /src
+COPY . .
+
+RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text
+RUN dotnet build -c Release -o /app
+
+FROM build AS publish
+RUN dotnet publish -c Release -o /app
+
+FROM base AS final
+WORKDIR /app
+COPY --from=publish /app .
+ENV COMPlus_EnableDiagnostics=0
+ENV DOTNET_USE_POLLING_FILE_WATCHER=true
+
+ENTRYPOINT ["dotnet", "Api.PolicyHeaders.Robots.dll"]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/LICENSE b/Api.PolicyHeaders.Robots/LICENSE
new file mode 100644
index 00000000..006e8c21
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/LICENSE
@@ -0,0 +1,18 @@
+The MIT License (MIT)
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/README.md b/Api.PolicyHeaders.Robots/README.md
new file mode 100644
index 00000000..db3d38e3
--- /dev/null
+++ b/Api.PolicyHeaders.Robots/README.md
@@ -0,0 +1,48 @@
+# Api.PolicyHeaders.Robots
+
+> _Nano API application with robots options._  
+_All lessons are complete, self-contained examples that include build and deployment setup._
+
+> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. 
+Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._
+
+> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio.
+
+> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**.
+
+***
+
+## Table of Contents
+* [Summary](#summary)
+* [Configuration](#configuration)
+
+## Summary
+This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller 
+that inherits from the top-level Nano `BaseController`.  
+
+This example shows the `X-Robots-Tag` header being set.  
+
+The following endpoint is available for testing.  
+
+| Endpoint                                     | Description                                                                |
+| -------------------------------------------- | -------------------------------------------------------------------------- |
+| `http://localhost:8080/api/examples/nosniff` | Returns a `200 OK` response including the `X-Robots-Tag` response header.  |
+
+> 📖 Learn more about **[Nano Content Type Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#robots)**.
+
+## Configuration
+```json
+"App": {
+  "HttpPolicyHeaders": {
+    "Robots": {
+      "UseNoIndex": true,
+      "UseNoFollow": true,
+      "UseNoSnippet": true,
+      "UseNoArchive": true,
+      "UseNoOdp": true,
+      "UseNoTranslate": true,
+      "UseNoImageIndex": true
+    }
+  }
+}
+```
\ No newline at end of file
diff --git a/Api.PolicyHeaders.Robots/icon.png b/Api.PolicyHeaders.Robots/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5
GIT binary patch
literal 14103
zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA|
z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw
z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j
z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+
z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm>
zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X<
z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-)
zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS
z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu(
z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm
ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!%
zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17
z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2&
zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ<
zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0
z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G
zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W
zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P
z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d
zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c
z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I|
z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG
zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk
z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J
z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I
zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^
zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq
zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX
z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f
zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c
zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY
zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O!
z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB
znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe
zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T-
zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD
z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW
z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy
zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8
zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE
zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG
z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ
zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2
z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz
zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T
zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5
zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ
za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G
zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM
zYvyNIF@BQL&)J13%9sMP2^X4o#Bp
zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB;
zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB
z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c
z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^
z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d
z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_
zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC
zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795
zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT
z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z
zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo
zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio
zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H
zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`=
z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4
z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji
z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k
z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S
z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;#
zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi
z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp
zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc}
zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e
zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf
z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6
zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0
zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_
ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF
z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V
z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`)
zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX
z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j
z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM
z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2
zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD#
zh8+*+}dEu#u^Pimdn-#J5*7
zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV
ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@
z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J
zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p
zt~FD{A7h_~
zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3
z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u
z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K
zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+
zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$
zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC
zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV
zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$=
zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap
zYdU!r0OmqEEhiSY*D_s~DtFK(CN
zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL
zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y&
zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X
z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF!
z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760
zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#?
z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m
zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK
z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v
zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S
ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g
z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s
z9Lh6K34s%w_%Sy+u1Eso_<1Eq&
z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ
zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@
zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki
zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@
z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR
z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s
z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI
z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv
z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo
zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e
zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3
zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy
z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER
zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW%
zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO
z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n
zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A
zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F
zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK!
zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@
zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@
zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T)
za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95
z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk
zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r|
znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is
zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_
zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)(
zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i
zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6
zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_
z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7
z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j
zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O
zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE
zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e
z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT
z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz%
z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D
z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG
z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W
zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM<
zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V
z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV
z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n
zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o<
zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~
+
+  
+    2.1
+    Linux
+    false
+    http://localhost:{ServicePort}/docs
+    $(ProjectName)
+  
+  
+    
+  
+
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.docker/docker-compose.yml b/Api.PolicyHeaders.XssProtection/.docker/docker-compose.yml
new file mode 100644
index 00000000..01f78aba
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.docker/docker-compose.yml
@@ -0,0 +1,17 @@
+services:
+  api.policyheaders.xssprotection:
+    image: api.policyheaders.xssprotection
+    hostname: api-policyheaders-xssprotection
+    restart: on-failure
+    ports:
+      - 8080:8080
+    build:
+      context: ../Api.PolicyHeaders.XssProtection
+      dockerfile: "Dockerfile.Local"
+    networks:
+      - network
+
+networks:
+  network:
+    name: network
+    driver: bridge
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.dockerignore b/Api.PolicyHeaders.XssProtection/.dockerignore
new file mode 100644
index 00000000..e694ae21
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.dockerignore
@@ -0,0 +1,12 @@
+.dockerignore
+.env
+.git
+.gitignore
+.vs
+.vscode
+docker-compose.yml
+docker-compose.*.yml
+*/bin
+*/obj
+!obj/Docker/publish/*
+!obj/Docker/empty/
diff --git a/Api.PolicyHeaders.XssProtection/.github/config/slack.yml b/Api.PolicyHeaders.XssProtection/.github/config/slack.yml
new file mode 100644
index 00000000..3592affb
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.github/config/slack.yml
@@ -0,0 +1,18 @@
+username: GitHub Actions
+icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png
+
+pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>."
+
+text: |
+  {{#if payload.commits}}
+  *Commits*
+  {{#each payload.commits}}
+  <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}}
+  {{/each}}
+  {{/if}}
+
+footer: >-
+  <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}}
+  
+fallback: |-
+  [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}}
diff --git a/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml b/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml
new file mode 100644
index 00000000..8673f1f9
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.github/workflows/build-and-deploy.yml
@@ -0,0 +1,179 @@
+name: Build And Deploy
+on:
+  pull_request:
+    branches:
+      - master 
+  push:
+    branches:
+      - master 
+env:
+  APP_NAME: Api.PolicyHeaders.XssProtection
+  IMAGE_NAME: api.policyheaders.xssprotection
+  SERVICE_NAME: api-policyheaders-xssprotection
+  VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}'
+  DOTNET_SDK_VERSION: 10.0
+  DOTNET_ASPNET_VERSION: 10.0
+  AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }}
+  AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
+  AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
+  AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
+  AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }}
+  NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
+  NUGET_USERNAME: ${{ github.actor }}
+  NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+  CONTAINER_REGISTRY_HOST: ghcr.io
+  CONTAINER_REGISTRY_USERNAME: ${{ github.actor }}
+  CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
+  CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }}
+  KUBERNETES_NODEPOOL_COMPUTE: cpu
+  KUBERNETES_NAMESPACE: apps
+  KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }}
+  KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }}
+  KUBERNETES_REPLICA_HISTORY_COUNT: 0
+  KUBERNETES_MEMORY_REQUEST: 512Mi   
+  KUBERNETES_MEMORY_LIMIT: 1536Mi
+  KUBERNETES_MEMORY_SCALING: 180
+  KUBERNETES_CPU_REQUEST: 200m
+  KUBERNETES_CPU_LIMIT: 600m
+  KUBERNETES_CPU_SCALING: 180
+  ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }}
+jobs:
+  build-and-deploy:
+    runs-on:
+      - self-hosted
+      - linux 
+      - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }}
+    permissions:
+      contents: write
+      packages: write
+      id-token: write
+    concurrency:
+      group: ${{ github.repository }}
+      cancel-in-progress: true
+    steps:
+      - uses: actions/checkout@v6
+
+      - name: Azure Login
+        shell: pwsh
+        run: |
+          az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none;
+          az account set -s $env:AZURE_SUBSCRIPTION_ID -o none;
+
+          $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv;
+          az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none;
+
+      - name: Build
+        shell: pwsh
+        run: |
+          dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text;
+
+          dotnet build -c Release .\$env:APP_NAME.sln;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+      - name: Test
+        shell: pwsh
+        run: |
+          dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj;
+          if ($LastExitCode -ne 0)
+          { 
+            throw "error";
+          };
+
+      - name: Publish Image
+        shell: pwsh
+        run: |
+          $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower()
+          $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest";
+          $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION
+          
+          docker build `
+              -t $imageLatestTag `
+              -t $imageVersionTag `
+              --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION `
+              --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION `
+              --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL `
+              --build-arg NUGET_HOST=$env:NUGET_HOST `
+              --build-arg NUGET_USERNAME=$env:NUGET_USERNAME `
+              --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD `
+              ./;
+
+          if ($LastExitCode -ne 0) 
+          { 
+              throw "error";
+          };
+
+          echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin;
+          docker push $imageLatestTag;
+          docker push $imageVersionTag;
+          if ($LastExitCode -ne 0) 
+          { 
+              throw "error";
+          };  
+
+      - name: Publish NuGet
+        shell: pwsh
+        run: |
+          $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj";
+          dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build;
+          dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD;
+          if ($LastExitCode -ne 0) 
+          { 
+              throw "error";
+          };  
+
+      - name: Kubernetes Deploy
+        shell: pwsh
+        run: |
+          Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml;
+          kubectl apply -f .kubernetes/service.tmp.yaml;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+          Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml;
+          kubectl apply -f .kubernetes/configmap.tmp.yaml;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+          Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml;
+          kubectl apply -f .kubernetes/deployment.tmp.yaml;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+          Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml;
+          kubectl apply -f .kubernetes/autoscaler.tmp.yaml;
+          if ($LastExitCode -ne 0)
+          { 
+              throw "error";
+          };
+
+      - name: GitHub Release
+        if: github.ref == 'refs/heads/master'
+        uses: ncipollo/release-action@v1
+        with:
+          tag: v${{ env.VERSION }}
+          name: "Release ${{ env.VERSION }}"
+          body: |
+            Version: ${{ env.VERSION }}
+            Commit: ${{ github.sha }}
+          artifacts: "nupkgs/*"
+          token: ${{ secrets.GITHUB_TOKEN }}
+          draft: false
+          prerelease: false
+
+      - name: Slack Notification
+        if: always()
+        uses: act10ns/slack@v2
+        with:
+          webhook-url: ${{ secrets.SLACK_WEBHOOK }}
+          config: .github/config/slack.yml
+          status: ${{ job.status }}
+          channel: ${{ vars.SLACK_CHANNEL }}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.gitignore b/Api.PolicyHeaders.XssProtection/.gitignore
new file mode 100644
index 00000000..9921fc06
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.gitignore
@@ -0,0 +1,11 @@
+.vs
+*.user
+*.userprefs
+*.suo
+_ReSharper*
+**/bin
+**/obj
+*.DotSettings.User
+packages
+.env
+**/Properties/launchSettings.json
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/autoscaler.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/autoscaler.yaml
new file mode 100644
index 00000000..95add8ad
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.kubernetes/autoscaler.yaml
@@ -0,0 +1,25 @@
+apiVersion: autoscaling/v2
+kind: HorizontalPodAutoscaler
+metadata:
+  name: %SERVICE_NAME%-hpa
+  namespace: %KUBERNETES_NAMESPACE%
+spec:
+  minReplicas: %KUBERNETES_REPLICA_COUNT%
+  maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX%
+  scaleTargetRef:
+    apiVersion: apps/v1
+    kind: Deployment
+    name: %SERVICE_NAME%
+  metrics:
+    - type: Resource
+      resource:
+        name: cpu
+        target:
+          type: Utilization
+          averageUtilization: %KUBERNETES_CPU_SCALING%
+    - type: Resource
+      resource:
+        name: memory
+        target:
+          type: Utilization
+          averageUtilization: %KUBERNETES_MEMORY_SCALING%
diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml
new file mode 100644
index 00000000..3977c0ee
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.kubernetes/configmap.yaml
@@ -0,0 +1,8 @@
+  apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: %SERVICE_NAME%-config
+  namespace: %KUBERNETES_NAMESPACE%
+data:
+  App__Version: %VERSION%
+  ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT%
diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml
new file mode 100644
index 00000000..02882c56
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.kubernetes/deployment.yaml
@@ -0,0 +1,78 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: %SERVICE_NAME%
+  namespace: %KUBERNETES_NAMESPACE%
+  labels:
+    app: %SERVICE_NAME%
+spec:
+  replicas: %KUBERNETES_REPLICA_COUNT%
+  revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT%
+  selector:
+    matchLabels:
+      app: %SERVICE_NAME%
+  template:
+    metadata:
+      labels:
+        app: %SERVICE_NAME%
+    spec:
+      automountServiceAccountToken: false
+      securityContext:
+        runAsUser: 1000
+        runAsGroup: 2000 
+        fsGroup: 2000
+      topologySpreadConstraints:
+        - maxSkew: 1
+          topologyKey: kubernetes.io/hostname
+          whenUnsatisfiable: ScheduleAnyway
+          labelSelector:
+            matchLabels:
+              app: %SERVICE_NAME%  
+      nodeSelector:
+        nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE%
+        kubernetes.io/os: linux
+      containers:
+      - name: %SERVICE_NAME%
+        image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION%
+        ports:
+        - containerPort: 8080
+        imagePullPolicy: Always 
+        envFrom:
+        - configMapRef:
+            name: %SERVICE_NAME%-config
+        resources:
+          requests:
+            memory: %KUBERNETES_MEMORY_REQUEST%
+            cpu: %KUBERNETES_CPU_REQUEST%
+          limits:
+            memory: %KUBERNETES_MEMORY_LIMIT%
+            cpu: %KUBERNETES_CPU_LIMIT%
+        securityContext:
+          privileged: false
+          allowPrivilegeEscalation: false
+          readOnlyRootFilesystem: true
+          runAsNonRoot: true
+          runAsUser: 1000
+          runAsGroup: 2000
+          capabilities:
+            drop:
+            - ALL
+        livenessProbe:
+          httpGet:
+            path: /healthz
+            port: 8080
+            scheme: HTTP
+          periodSeconds: 10
+          initialDelaySeconds: 30
+          timeoutSeconds: 2
+        readinessProbe:
+          httpGet:
+            path: /healthz
+            port: 8080
+            scheme: HTTP
+          periodSeconds: 5
+          initialDelaySeconds: 20
+          timeoutSeconds: 2
+      imagePullSecrets:
+        - name: ghcr-pull-secret
+
diff --git a/Api.PolicyHeaders.XssProtection/.kubernetes/service.yaml b/Api.PolicyHeaders.XssProtection/.kubernetes/service.yaml
new file mode 100644
index 00000000..2d8e20b2
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.kubernetes/service.yaml
@@ -0,0 +1,12 @@
+apiVersion: v1
+kind: Service
+metadata:
+  name: %SERVICE_NAME%
+  namespace: %KUBERNETES_NAMESPACE%
+spec:
+  ports:  
+  - name: http
+    port: 8080
+  selector:
+    app: %SERVICE_NAME%
+  type: ClusterIP
diff --git a/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Properties/DoNotParallelize.cs b/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Properties/DoNotParallelize.cs
new file mode 100644
index 00000000..6a076669
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Properties/DoNotParallelize.cs
@@ -0,0 +1,3 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+[assembly: DoNotParallelize]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Tests.Api.PolicyHeaders.XssProtection.csproj b/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Tests.Api.PolicyHeaders.XssProtection.csproj
new file mode 100644
index 00000000..a1a6ef58
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/.tests/Tests.Api.PolicyHeaders.XssProtection/Tests.Api.PolicyHeaders.XssProtection.csproj
@@ -0,0 +1,23 @@
+
+
+	
+		net10.0
+		false
+		latest
+		
+		
+		true
+	
+
+	
+		
+		
+		
+	
+
+	
+	  
+	  
+	
+
+
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj
new file mode 100644
index 00000000..890be2f1
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.Models/Api.PolicyHeaders.XssProtection.Models.csproj
@@ -0,0 +1,75 @@
+
+
+	
+		net10.0
+		
+		10.0.0.0
+		10.0.0.0
+		10.0.0.0
+		AnyCPU
+		Debug;Release
+		latest
+		en-US
+		enable
+		LICENSE
+		true
+		
+		True
+		true
+		true
+		true
+		Michael Vivet
+		Michael Vivet
+		$(ProjectName)
+		Example project demonstrating a focused aspect of the Nano Library.
+		
+			A practical example highlighting a specific area or scenario of Nano for learning
+			and experimentation. Shows how features or patterns can be applied without
+			representing a full application or complete reference.
+		
+		
+			- Added .NET 10 support
+		
+		git
+		master
+		https://github.com/Nano-Core/Nano.Examples.git
+		$(GitCommitHash)
+		true
+		true
+		true
+		snupkg
+		true
+		$(Version)
+		nano;example;sample;framework;library;learning;showcase
+		https://github.com/Nano-Core/Nano.Library/$(ProjectName)
+		LICENSE
+		icon.png
+		README.md
+		true
+	
+
+	
+		True
+	
+
+	
+		
+		
+		
+	
+
+	
+		
+	
+
+	
+	  
+	  
+	  
+	  
+	  
+	  
+	  
+	
+
+
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.sln b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.sln
new file mode 100644
index 00000000..29aeb199
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.sln
@@ -0,0 +1,134 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 18
+VisualStudioVersion = 18.1.11312.151
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}"
+	ProjectSection(SolutionItems) = preProject
+		.dockerignore = .dockerignore
+		.gitignore = .gitignore
+		Dockerfile = Dockerfile
+		icon.png = icon.png
+		LICENSE = LICENSE
+		README.md = README.md
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}"
+	ProjectSection(SolutionItems) = preProject
+		.kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml
+		.kubernetes\configmap.yaml = .kubernetes\configmap.yaml
+		.kubernetes\deployment.yaml = .kubernetes\deployment.yaml
+		.kubernetes\service.yaml = .kubernetes\service.yaml
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}"
+	ProjectSection(SolutionItems) = preProject
+		.github\config\slack.yml = .github\config\slack.yml
+	EndProjectSection
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}"
+	ProjectSection(SolutionItems) = preProject
+		.github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml
+	EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.XssProtection.Models", "Api.PolicyHeaders.XssProtection.Models\Api.PolicyHeaders.XssProtection.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.PolicyHeaders.XssProtection", "Api.PolicyHeaders.XssProtection\Api.PolicyHeaders.XssProtection.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.PolicyHeaders.XssProtection", ".tests\Tests.Api.PolicyHeaders.XssProtection\Tests.Api.PolicyHeaders.XssProtection.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{6C7D58FA-03E4-406C-ACB7-87EAE221099A}"
+EndProject
+Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{345A819B-8EA9-B4BD-056F-377089EA0181}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU
+		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU
+		{345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{345A819B-8EA9-B4BD-056F-377089EA0181}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{345A819B-8EA9-B4BD-056F-377089EA0181}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A}
+		{F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A}
+		{F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}
+		{6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1}
+		{345A819B-8EA9-B4BD-056F-377089EA0181} = {6C7D58FA-03E4-406C-ACB7-87EAE221099A}
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE}
+	EndGlobalSection
+EndGlobal
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.csproj b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.csproj
new file mode 100644
index 00000000..792b3a4e
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection.csproj
@@ -0,0 +1,37 @@
+
+
+	
+		net10.0
+		
+		10.0.0.0
+		10.0.0.0
+		10.0.0.0
+		AnyCPU
+		Debug;Release
+		latest
+		en-US
+		enable
+		LICENSE
+		true
+		
+		false
+		true
+		false
+		true
+		Linux
+		..\docker-compose.dcproj
+	
+
+	
+		True
+	
+
+	
+		
+	
+
+	
+	    
+	
+
+
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Controllers/ExamplesController.cs b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Controllers/ExamplesController.cs
new file mode 100644
index 00000000..671d077e
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Controllers/ExamplesController.cs
@@ -0,0 +1,31 @@
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using Nano.App.Api.Controllers;
+
+namespace Api.PolicyHeaders.XssProtection.Controllers;
+
+/// 
+/// Controller with examples.
+/// 
+/// The .
+public class ExamplesController(ILogger logger) : BaseController(logger)
+{
+    /// 
+    /// Xss Action.
+    /// 
+    /// The cancellation token.
+    /// A message.
+    /// Success.
+    [HttpGet]
+    [Route("xss")]
+    [ProducesResponseType((int)HttpStatusCode.OK)]
+    public virtual async Task XssAsync(CancellationToken cancellationToken = default)
+    {
+        await Task.CompletedTask;
+
+        return this.Ok("xss");
+    }
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local
new file mode 100644
index 00000000..0564fe65
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Dockerfile.Local
@@ -0,0 +1,6 @@
+ARG DOTNET_ASPNET_VERSION="10.0"
+FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base
+
+EXPOSE 8080
+
+ENTRYPOINT ["dotnet", "Api.PolicyHeaders.XssProtection.dll"]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Program.cs b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Program.cs
new file mode 100644
index 00000000..32865375
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Program.cs
@@ -0,0 +1,10 @@
+using Nano.App.Api;
+
+NanoApiApplication
+    .ConfigureApp()
+    .ConfigureServices(_ =>
+    {
+        // Blank
+    })
+    .Build()
+    .Run();
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Properties/InternalsVisibleTo.cs b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Properties/InternalsVisibleTo.cs
new file mode 100644
index 00000000..bc3b7710
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/Properties/InternalsVisibleTo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Tests.Api.PolicyHeaders.XssProtection")]
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Development.json b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Development.json
new file mode 100644
index 00000000..8593c62d
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Development.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Production.json b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Production.json
new file mode 100644
index 00000000..8593c62d
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Production.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Staging.json b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Staging.json
new file mode 100644
index 00000000..8593c62d
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.Staging.json
@@ -0,0 +1,2 @@
+{
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.json b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.json
new file mode 100644
index 00000000..9c580799
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/appsettings.json
@@ -0,0 +1,19 @@
+{
+  "App": {
+    "Version": "1.0.0.0",
+    "Hosting": {
+      "Root": "api",
+      "Http": {
+        "Ports": [
+          8080
+        ]
+      }
+    },
+    "HttpPolicyHeaders": {
+      "XssProtection": {
+        "XssProtectionPolicyHeader": "FilterEnabledBlockMode",
+        "ReportingUrl": null
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/wwwroot/xss-violation.html b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/wwwroot/xss-violation.html
new file mode 100644
index 00000000..288c3666
--- /dev/null
+++ b/Api.PolicyHeaders.XssProtection/Api.PolicyHeaders.XssProtection/wwwroot/xss-violation.html
@@ -0,0 +1,22 @@
+
+
+
+    
+    XSS Protection Test
+
+
+    

XSS Protection Test

+ +

If X-XSS-Protection is working with block mode, this page should NOT render and the no alert should show.

+ + + + \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/Dockerfile b/Api.PolicyHeaders.XssProtection/Dockerfile new file mode 100644 index 00000000..b9e1371d --- /dev/null +++ b/Api.PolicyHeaders.XssProtection/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.PolicyHeaders.XssProtection.dll"] \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/LICENSE b/Api.PolicyHeaders.XssProtection/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.PolicyHeaders.XssProtection/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/README.md b/Api.PolicyHeaders.XssProtection/README.md new file mode 100644 index 00000000..4070cb9f --- /dev/null +++ b/Api.PolicyHeaders.XssProtection/README.md @@ -0,0 +1,49 @@ +# Api.PolicyHeaders.XssProtection + +> _Nano API application with xxs protection._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +Run the endpoint to inspect the `X-XSS-Protection` header in the response. Then load the provided HTML page to observe that the `alert` is blocked by the browser. + +Note that `X-XSS-Protection` is deprecated and ignored by modern browsers, including: +* Chrome +* Edge (Chromium) +* Firefox + +> ⚠️ This header is considered a legacy defense-in-depth mechanism and has been largely replaced by `Content-Security-Policy`. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ---------------------------------------- | ------------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/xss` | Returns a `200 OK` response including the `X-XSS-Protection` response header. | + +> 📖 Learn more about **[Nano Xxs Protection Header](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#xxs-protection)**. + +## Configuration +```json +"App": { + "HttpPolicyHeaders": { + "XssProtection": { + "XssProtectionPolicyHeader": "FilterEnabledBlockMode" + } + } +} +``` \ No newline at end of file diff --git a/Api.PolicyHeaders.XssProtection/icon.png b/Api.PolicyHeaders.XssProtection/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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
+ + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.RequestTracing/.docker/docker-compose.yml b/Api.RequestTracing/.docker/docker-compose.yml new file mode 100644 index 00000000..a0cfd6ed --- /dev/null +++ b/Api.RequestTracing/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.requesttracing: + image: api.requesttracing + hostname: api-requesttracing + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.RequestTracing + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.RequestTracing/.dockerignore b/Api.RequestTracing/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.RequestTracing/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.RequestTracing/.github/config/slack.yml b/Api.RequestTracing/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.RequestTracing/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.RequestTracing/.github/workflows/build-and-deploy.yml b/Api.RequestTracing/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..0f92a439 --- /dev/null +++ b/Api.RequestTracing/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.RequestTracing + IMAGE_NAME: api.requesttracing + SERVICE_NAME: api-requesttracing + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.RequestTracing/.gitignore b/Api.RequestTracing/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.RequestTracing/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.RequestTracing/.kubernetes/autoscaler.yaml b/Api.RequestTracing/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.RequestTracing/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.RequestTracing/.kubernetes/configmap.yaml b/Api.RequestTracing/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.RequestTracing/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.RequestTracing/.kubernetes/deployment.yaml b/Api.RequestTracing/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.RequestTracing/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.RequestTracing/.kubernetes/service.yaml b/Api.RequestTracing/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.RequestTracing/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Properties/DoNotParallelize.cs b/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Tests.Api.RequestTracing.csproj b/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Tests.Api.RequestTracing.csproj new file mode 100644 index 00000000..45afd36c --- /dev/null +++ b/Api.RequestTracing/.tests/Tests.Api.RequestTracing/Tests.Api.RequestTracing.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj b/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing.Models/Api.RequestTracing.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing.sln b/Api.RequestTracing/Api.RequestTracing.sln new file mode 100644 index 00000000..ad4eefca --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.RequestTracing.Models", "Api.RequestTracing.Models\Api.RequestTracing.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.RequestTracing", "Api.RequestTracing\Api.RequestTracing.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.RequestTracing", ".tests\Tests.Api.RequestTracing\Tests.Api.RequestTracing.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.RequestTracing/Api.RequestTracing/Api.RequestTracing.csproj b/Api.RequestTracing/Api.RequestTracing/Api.RequestTracing.csproj new file mode 100644 index 00000000..6810272d --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/Api.RequestTracing.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/Controllers/ExamplesController.cs b/Api.RequestTracing/Api.RequestTracing/Controllers/ExamplesController.cs new file mode 100644 index 00000000..dd3f2e90 --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/Controllers/ExamplesController.cs @@ -0,0 +1,34 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.RequestTracing.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Request Tracing Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("request-tracing")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task RequestTracingAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok(new + { + this.RequestId + }); + } +} \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local b/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local new file mode 100644 index 00000000..87cc5040 --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.RequestTracing.dll"] \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/Program.cs b/Api.RequestTracing/Api.RequestTracing/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.RequestTracing/Api.RequestTracing/Properties/InternalsVisibleTo.cs b/Api.RequestTracing/Api.RequestTracing/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..fae87874 --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.RequestTracing")] \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/appsettings.Development.json b/Api.RequestTracing/Api.RequestTracing/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/appsettings.Production.json b/Api.RequestTracing/Api.RequestTracing/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/appsettings.Staging.json b/Api.RequestTracing/Api.RequestTracing/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.RequestTracing/Api.RequestTracing/appsettings.json b/Api.RequestTracing/Api.RequestTracing/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.RequestTracing/Api.RequestTracing/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.RequestTracing/Dockerfile b/Api.RequestTracing/Dockerfile new file mode 100644 index 00000000..316aafa3 --- /dev/null +++ b/Api.RequestTracing/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.RequestTracing.dll"] \ No newline at end of file diff --git a/Api.RequestTracing/LICENSE b/Api.RequestTracing/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.RequestTracing/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.RequestTracing/README.md b/Api.RequestTracing/README.md new file mode 100644 index 00000000..60eb9dd7 --- /dev/null +++ b/Api.RequestTracing/README.md @@ -0,0 +1,31 @@ +# Api.RequestTracing + +> _Nano API application with request tracing._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +Try passing the `X-Request-Id` request header and observe that Nano uses it and returns the same value in the response. +If no `X-Request-Id` header is provided, Nano automatically generates one and returns it in the response. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ----------------------------------------------------- | -------------------------------------- | +| `http://localhost:8080/api/examples/request-tracing` | Returns a simple `200 OK` response. | + +> 📖 Learn more about **[Nano Request Tracing](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#request-tracing)**. diff --git a/Api.RequestTracing/icon.png b/Api.RequestTracing/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.ResponseCache/.docker/docker-compose.yml b/Api.ResponseCache/.docker/docker-compose.yml new file mode 100644 index 00000000..641f6e5b --- /dev/null +++ b/Api.ResponseCache/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.responsecache: + image: api.responsecache + hostname: api-responsecache + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.ResponseCache + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.ResponseCache/.dockerignore b/Api.ResponseCache/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.ResponseCache/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.ResponseCache/.github/config/slack.yml b/Api.ResponseCache/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.ResponseCache/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ResponseCache/.github/workflows/build-and-deploy.yml b/Api.ResponseCache/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..b01ba103 --- /dev/null +++ b/Api.ResponseCache/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.ResponseCache + IMAGE_NAME: api.responsecache + SERVICE_NAME: api-responsecache + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ResponseCache/.gitignore b/Api.ResponseCache/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.ResponseCache/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ResponseCache/.kubernetes/autoscaler.yaml b/Api.ResponseCache/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.ResponseCache/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ResponseCache/.kubernetes/configmap.yaml b/Api.ResponseCache/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.ResponseCache/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ResponseCache/.kubernetes/deployment.yaml b/Api.ResponseCache/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.ResponseCache/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.ResponseCache/.kubernetes/service.yaml b/Api.ResponseCache/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.ResponseCache/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Properties/DoNotParallelize.cs b/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Tests.Api.ResponseCache.csproj b/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Tests.Api.ResponseCache.csproj new file mode 100644 index 00000000..9df93a74 --- /dev/null +++ b/Api.ResponseCache/.tests/Tests.Api.ResponseCache/Tests.Api.ResponseCache.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj b/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache.Models/Api.ResponseCache.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache.sln b/Api.ResponseCache/Api.ResponseCache.sln new file mode 100644 index 00000000..c2ea2ac0 --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ResponseCache.Models", "Api.ResponseCache.Models\Api.ResponseCache.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ResponseCache", "Api.ResponseCache\Api.ResponseCache.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ResponseCache", ".tests\Tests.Api.ResponseCache\Tests.Api.ResponseCache.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.ResponseCache/Api.ResponseCache/Api.ResponseCache.csproj b/Api.ResponseCache/Api.ResponseCache/Api.ResponseCache.csproj new file mode 100644 index 00000000..92eed437 --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/Api.ResponseCache.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/Controllers/ExamplesController.cs b/Api.ResponseCache/Api.ResponseCache/Controllers/ExamplesController.cs new file mode 100644 index 00000000..835cd98f --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/Controllers/ExamplesController.cs @@ -0,0 +1,48 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.ResponseCache.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Response Cache Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("response-cache")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task ResponseCacheAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("response-cache"); + } + + /// + /// No Response Cache Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("no-response-cache")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task NoResponseCacheAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("no-response-cache"); + } +} \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local b/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local new file mode 100644 index 00000000..7062f25b --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ResponseCache.dll"] \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/Program.cs b/Api.ResponseCache/Api.ResponseCache/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ResponseCache/Api.ResponseCache/Properties/InternalsVisibleTo.cs b/Api.ResponseCache/Api.ResponseCache/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..9db9ee24 --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ResponseCache")] \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/appsettings.Development.json b/Api.ResponseCache/Api.ResponseCache/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/appsettings.Production.json b/Api.ResponseCache/Api.ResponseCache/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/appsettings.Staging.json b/Api.ResponseCache/Api.ResponseCache/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ResponseCache/Api.ResponseCache/appsettings.json b/Api.ResponseCache/Api.ResponseCache/appsettings.json new file mode 100644 index 00000000..239b0b01 --- /dev/null +++ b/Api.ResponseCache/Api.ResponseCache/appsettings.json @@ -0,0 +1,18 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "ResponseCache": { + "MaxSize": 1024, + "MaxBodySize": 102400, + "MaxAge": "00:20:00" + } + } +} \ No newline at end of file diff --git a/Api.ResponseCache/Dockerfile b/Api.ResponseCache/Dockerfile new file mode 100644 index 00000000..3dbc645f --- /dev/null +++ b/Api.ResponseCache/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.ResponseCache.dll"] \ No newline at end of file diff --git a/Api.ResponseCache/LICENSE b/Api.ResponseCache/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.ResponseCache/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.ResponseCache/README.md b/Api.ResponseCache/README.md new file mode 100644 index 00000000..9f7cecfe --- /dev/null +++ b/Api.ResponseCache/README.md @@ -0,0 +1,43 @@ +# Api.ResponseCache + +> _Nano API application with response cache._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example demonstrates using response caching with a Nano application. + +The following endpoint is available for testing. + +| Endpoint | Description | +| -------------------------------------------------------| ------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/response-cache` | Returns a `200 OK` response cached. Header: `Cache-Control=public, max-age=1200` | +| `http://localhost:8080/api/examples/no-response-cache` | Returns a `200 OK` response with no cache using Header: `[ResponseCache]`. `Cache-Control=no-store,no-cache` | + +> 📖 Learn more about **[Nano Response Cache](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#response-cache)**. + +## Configuration +```json +"App": { + "ResponseCache": { + "MaxSize": 1024, + "MaxBodySize": 102400, + "MaxAge": "00:20:00" + } +} +``` diff --git a/Api.ResponseCache/icon.png b/Api.ResponseCache/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.ResponseCompression/.docker/docker-compose.yml b/Api.ResponseCompression/.docker/docker-compose.yml new file mode 100644 index 00000000..e88bc020 --- /dev/null +++ b/Api.ResponseCompression/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.responsecompression: + image: api.responsecompression + hostname: api-responsecompression + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.ResponseCompression + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.ResponseCompression/.dockerignore b/Api.ResponseCompression/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.ResponseCompression/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.ResponseCompression/.github/config/slack.yml b/Api.ResponseCompression/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.ResponseCompression/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.ResponseCompression/.github/workflows/build-and-deploy.yml b/Api.ResponseCompression/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..5006980a --- /dev/null +++ b/Api.ResponseCompression/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.ResponseCompression + IMAGE_NAME: api.responsecompression + SERVICE_NAME: api-responsecompression + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.ResponseCompression/.gitignore b/Api.ResponseCompression/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.ResponseCompression/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.ResponseCompression/.kubernetes/autoscaler.yaml b/Api.ResponseCompression/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.ResponseCompression/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.ResponseCompression/.kubernetes/configmap.yaml b/Api.ResponseCompression/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.ResponseCompression/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.ResponseCompression/.kubernetes/deployment.yaml b/Api.ResponseCompression/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.ResponseCompression/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.ResponseCompression/.kubernetes/service.yaml b/Api.ResponseCompression/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.ResponseCompression/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Properties/DoNotParallelize.cs b/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Tests.Api.ResponseCompression.csproj b/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Tests.Api.ResponseCompression.csproj new file mode 100644 index 00000000..e0eb0a25 --- /dev/null +++ b/Api.ResponseCompression/.tests/Tests.Api.ResponseCompression/Tests.Api.ResponseCompression.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj b/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression.Models/Api.ResponseCompression.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression.sln b/Api.ResponseCompression/Api.ResponseCompression.sln new file mode 100644 index 00000000..f93b7bfb --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ResponseCompression.Models", "Api.ResponseCompression.Models\Api.ResponseCompression.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.ResponseCompression", "Api.ResponseCompression\Api.ResponseCompression.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.ResponseCompression", ".tests\Tests.Api.ResponseCompression\Tests.Api.ResponseCompression.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.ResponseCompression/Api.ResponseCompression/Api.ResponseCompression.csproj b/Api.ResponseCompression/Api.ResponseCompression/Api.ResponseCompression.csproj new file mode 100644 index 00000000..ed8e4ad2 --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/Api.ResponseCompression.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/Controllers/ExamplesController.cs b/Api.ResponseCompression/Api.ResponseCompression/Controllers/ExamplesController.cs new file mode 100644 index 00000000..06c12f78 --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/Controllers/ExamplesController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Common.Consts; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Api.ResponseCompression.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Response Compression Action. + /// + /// The cancellation token. + /// 1024 KB content. + /// Success. + [HttpGet] + [Route("response-compression")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task CompressedAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + var block = new string('A', 1024); + var payload = string.Concat(Enumerable.Repeat(block, 1024)); + + return this.Content(payload, HttpContentType.TEXT); + } +} \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local b/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local new file mode 100644 index 00000000..53c17eb8 --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.ResponseCompression.dll"] \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/Program.cs b/Api.ResponseCompression/Api.ResponseCompression/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.ResponseCompression/Api.ResponseCompression/Properties/InternalsVisibleTo.cs b/Api.ResponseCompression/Api.ResponseCompression/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..19af86ea --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.ResponseCompression")] \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Development.json b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Production.json b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/appsettings.Staging.json b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.ResponseCompression/Api.ResponseCompression/appsettings.json b/Api.ResponseCompression/Api.ResponseCompression/appsettings.json new file mode 100644 index 00000000..79f4ab41 --- /dev/null +++ b/Api.ResponseCompression/Api.ResponseCompression/appsettings.json @@ -0,0 +1,17 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "ResponseCompression": { + "UseGzip": true, + "UseBrotli": true + } + } +} \ No newline at end of file diff --git a/Api.ResponseCompression/Dockerfile b/Api.ResponseCompression/Dockerfile new file mode 100644 index 00000000..e0f4803d --- /dev/null +++ b/Api.ResponseCompression/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.ResponseCompression.dll"] \ No newline at end of file diff --git a/Api.ResponseCompression/LICENSE b/Api.ResponseCompression/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.ResponseCompression/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.ResponseCompression/README.md b/Api.ResponseCompression/README.md new file mode 100644 index 00000000..fa87296c --- /dev/null +++ b/Api.ResponseCompression/README.md @@ -0,0 +1,42 @@ +# Api.ResponseCompression + +> _Nano API application with response compression._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +When the endpoint is invoked with the Accept-Encoding header set to gzip, deflate, br, the response will be compressed. +If this header is not present, the response will be sent uncompressed. + +The following endpoint is available for testing. + +| Endpoint | Description | +| --------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/response-compression` | Returns a `200 OK` response. Headers: `Content-Encoding: gzip` and `Vary: Accept-Encoding` | + +> 📖 Learn more about **[Nano Response Compression](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#response-compression)**. + +## Configuration +```json +"App": { + "ResponseCompression": { + "UseGzip": true, + "UseBrotli": true + } +} +``` diff --git a/Api.ResponseCompression/icon.png b/Api.ResponseCompression/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Session/.docker/docker-compose.yml b/Api.Session/.docker/docker-compose.yml new file mode 100644 index 00000000..3c71b4fb --- /dev/null +++ b/Api.Session/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.session: + image: api.session + hostname: api-session + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Session + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Session/.dockerignore b/Api.Session/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Session/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Session/.github/config/slack.yml b/Api.Session/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Session/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Session/.github/workflows/build-and-deploy.yml b/Api.Session/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..37ef11b0 --- /dev/null +++ b/Api.Session/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Session + IMAGE_NAME: api.session + SERVICE_NAME: api-session + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Session/.gitignore b/Api.Session/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Session/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Session/.kubernetes/autoscaler.yaml b/Api.Session/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Session/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Session/.kubernetes/configmap.yaml b/Api.Session/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Session/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Session/.kubernetes/deployment.yaml b/Api.Session/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Session/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Session/.kubernetes/service.yaml b/Api.Session/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Session/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Session/.tests/Tests.Api.Session/Properties/DoNotParallelize.cs b/Api.Session/.tests/Tests.Api.Session/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Session/.tests/Tests.Api.Session/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Session/.tests/Tests.Api.Session/Tests.Api.Session.csproj b/Api.Session/.tests/Tests.Api.Session/Tests.Api.Session.csproj new file mode 100644 index 00000000..62452e65 --- /dev/null +++ b/Api.Session/.tests/Tests.Api.Session/Tests.Api.Session.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Session/Api.Session.Models/Api.Session.Models.csproj b/Api.Session/Api.Session.Models/Api.Session.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Session/Api.Session.Models/Api.Session.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Session/Api.Session.sln b/Api.Session/Api.Session.sln new file mode 100644 index 00000000..ce11032c --- /dev/null +++ b/Api.Session/Api.Session.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Session.Models", "Api.Session.Models\Api.Session.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Session", "Api.Session\Api.Session.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Session", ".tests\Tests.Api.Session\Tests.Api.Session.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Session/Api.Session/Api.Session.csproj b/Api.Session/Api.Session/Api.Session.csproj new file mode 100644 index 00000000..1b059621 --- /dev/null +++ b/Api.Session/Api.Session/Api.Session.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Session/Api.Session/Controllers/ExamplesController.cs b/Api.Session/Api.Session/Controllers/ExamplesController.cs new file mode 100644 index 00000000..f06cc811 --- /dev/null +++ b/Api.Session/Api.Session/Controllers/ExamplesController.cs @@ -0,0 +1,82 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Api.Session.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + private const string SESSION_KEY = "TestKey"; + + /// + /// Set Session Action. + /// + /// The value to set in session. + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("set-session")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task SetSessionAsync([FromQuery][Required]string value, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + this.HttpContext.Session + .SetString(SESSION_KEY, value); + + return this.Ok("session set"); + } + + /// + /// Get Session Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("get-session")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task GetSession(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + var value = this.HttpContext.Session + .GetString(SESSION_KEY); + + if (value == null) + { + return Ok("session is empty or expired"); + } + + return Ok($"session value: {value}"); + } + + /// + /// Clear Session Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("clear-session")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task ClearSession(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + this.HttpContext.Session + .Clear(); + + return Ok("sesssion cleared"); + } +} \ No newline at end of file diff --git a/Api.Session/Api.Session/Dockerfile.Local b/Api.Session/Api.Session/Dockerfile.Local new file mode 100644 index 00000000..fb4b06aa --- /dev/null +++ b/Api.Session/Api.Session/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Session.dll"] \ No newline at end of file diff --git a/Api.Session/Api.Session/Program.cs b/Api.Session/Api.Session/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Session/Api.Session/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Session/Api.Session/Properties/InternalsVisibleTo.cs b/Api.Session/Api.Session/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..d1859b8f --- /dev/null +++ b/Api.Session/Api.Session/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Session")] \ No newline at end of file diff --git a/Api.Session/Api.Session/appsettings.Development.json b/Api.Session/Api.Session/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Session/Api.Session/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Session/Api.Session/appsettings.Production.json b/Api.Session/Api.Session/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Session/Api.Session/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Session/Api.Session/appsettings.Staging.json b/Api.Session/Api.Session/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Session/Api.Session/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Session/Api.Session/appsettings.json b/Api.Session/Api.Session/appsettings.json new file mode 100644 index 00000000..901094d0 --- /dev/null +++ b/Api.Session/Api.Session/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "Session": { + "Timeout": "00:20:00" + } + } +} \ No newline at end of file diff --git a/Api.Session/Dockerfile b/Api.Session/Dockerfile new file mode 100644 index 00000000..7b0f7c94 --- /dev/null +++ b/Api.Session/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Session.dll"] \ No newline at end of file diff --git a/Api.Session/LICENSE b/Api.Session/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Session/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Session/README.md b/Api.Session/README.md new file mode 100644 index 00000000..bb7269a1 --- /dev/null +++ b/Api.Session/README.md @@ -0,0 +1,42 @@ +# Api.Session + +> _Nano API application with session enabled._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example demonstrates using session with a Nano application. + +The following endpoints is available for testing. + +| Endpoint | Description | +| -------------------------------------------------- | --------------------------------------------------------- | +| `http://localhost:8080/api/examples/set-session` | Sets a session variable and returns a `200 OK`. | +| `http://localhost:8080/api/examples/get-session` | Gets the session variable if set and returns a `200 OK`. | +| `http://localhost:8080/api/examples/clear-session` | Clear the session and returns a `200 OK`. | + +> 📖 Learn more about **[Nano Session](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#session)**. + +## Configuration +```json +"App": { + "Session": { + "Timeout": "00:20:00" + } +} +``` \ No newline at end of file diff --git a/Api.Session/icon.png b/Api.Session/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.StartupTasks/.docker/docker-compose.yml b/Api.StartupTasks/.docker/docker-compose.yml new file mode 100644 index 00000000..37494314 --- /dev/null +++ b/Api.StartupTasks/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.startuptasks: + image: api.startuptasks + hostname: api-startuptasks + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.StartupTasks + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.StartupTasks/.dockerignore b/Api.StartupTasks/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.StartupTasks/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.StartupTasks/.github/config/slack.yml b/Api.StartupTasks/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.StartupTasks/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.StartupTasks/.github/workflows/build-and-deploy.yml b/Api.StartupTasks/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..07fe4bdc --- /dev/null +++ b/Api.StartupTasks/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.StartupTasks + IMAGE_NAME: api.startuptasks + SERVICE_NAME: api-startuptasks + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.StartupTasks/.gitignore b/Api.StartupTasks/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.StartupTasks/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.StartupTasks/.kubernetes/autoscaler.yaml b/Api.StartupTasks/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.StartupTasks/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.StartupTasks/.kubernetes/configmap.yaml b/Api.StartupTasks/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.StartupTasks/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.StartupTasks/.kubernetes/deployment.yaml b/Api.StartupTasks/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.StartupTasks/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.StartupTasks/.kubernetes/service.yaml b/Api.StartupTasks/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.StartupTasks/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Properties/DoNotParallelize.cs b/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Tests.Api.StartupTasks.csproj b/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Tests.Api.StartupTasks.csproj new file mode 100644 index 00000000..bef76c37 --- /dev/null +++ b/Api.StartupTasks/.tests/Tests.Api.StartupTasks/Tests.Api.StartupTasks.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj b/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks.Models/Api.StartupTasks.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks.sln b/Api.StartupTasks/Api.StartupTasks.sln new file mode 100644 index 00000000..a32b7109 --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.StartupTasks.Models", "Api.StartupTasks.Models\Api.StartupTasks.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.StartupTasks", "Api.StartupTasks\Api.StartupTasks.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.StartupTasks", ".tests\Tests.Api.StartupTasks\Tests.Api.StartupTasks.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.StartupTasks/Api.StartupTasks/Api.StartupTasks.csproj b/Api.StartupTasks/Api.StartupTasks/Api.StartupTasks.csproj new file mode 100644 index 00000000..d6660e37 --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/Api.StartupTasks.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/Controllers/ExamplesController.cs b/Api.StartupTasks/Api.StartupTasks/Controllers/ExamplesController.cs new file mode 100644 index 00000000..beb98c91 --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.StartupTasks.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Startup Tasks Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("startup-tasks")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task StartupTaskAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("startup-tasks"); + } +} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local b/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local new file mode 100644 index 00000000..1a5748f0 --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.StartupTasks.dll"] \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/Program.cs b/Api.StartupTasks/Api.StartupTasks/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.StartupTasks/Api.StartupTasks/Properties/InternalsVisibleTo.cs b/Api.StartupTasks/Api.StartupTasks/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..0d6b6c6a --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.StartupTasks")] \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/Startup/ExampleStartupTask.cs b/Api.StartupTasks/Api.StartupTasks/Startup/ExampleStartupTask.cs new file mode 100644 index 00000000..83be829c --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/Startup/ExampleStartupTask.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.StartUp; + +namespace Api.StartupTasks.Startup; + +/// +/// Example Startup Task. +/// +/// The . +public class ExampleStartupTask(ILogger logger) : BaseStartupTask(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + Console.WriteLine("Example Startup Task Started..."); + + await Task.Delay(20000, cancellationToken); + + Console.WriteLine(DateTime.UtcNow); + + Console.WriteLine("Example Startup Task Completed..."); + } +} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/appsettings.Development.json b/Api.StartupTasks/Api.StartupTasks/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/appsettings.Production.json b/Api.StartupTasks/Api.StartupTasks/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/appsettings.Staging.json b/Api.StartupTasks/Api.StartupTasks/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.StartupTasks/Api.StartupTasks/appsettings.json b/Api.StartupTasks/Api.StartupTasks/appsettings.json new file mode 100644 index 00000000..053b76d1 --- /dev/null +++ b/Api.StartupTasks/Api.StartupTasks/appsettings.json @@ -0,0 +1,15 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + } + } +} \ No newline at end of file diff --git a/Api.StartupTasks/Dockerfile b/Api.StartupTasks/Dockerfile new file mode 100644 index 00000000..13ab1f4e --- /dev/null +++ b/Api.StartupTasks/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.StartupTasks.dll"] \ No newline at end of file diff --git a/Api.StartupTasks/LICENSE b/Api.StartupTasks/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.StartupTasks/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.StartupTasks/README.md b/Api.StartupTasks/README.md new file mode 100644 index 00000000..ed826734 --- /dev/null +++ b/Api.StartupTasks/README.md @@ -0,0 +1,42 @@ +# Api.StartupTasks + +> _Nano API application with startup tasks._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +After approximately 20 seconds, the startup task will complete, and the health check endpoint will report a status of `Healthy`. + +The following endpoint is available for testing. + +| Endpoint | Description | +| --------------------------------------------------- | -------------------------------------- | +| `http://localhost:8080/api/examples/startup-tasks` | Returns a simple `200 OK` response. | + +> 📖 Learn more about **[Nano Startup Tasks](https://github.com/Nano-Core/tree/master/Nano.Library/Nano.App#startup-tasks)**. + +## Configuration +There is no configuration required for startup tasks themselves. +The health-check configuration has just been added to observe that the `self` health-check reports `Healthy`. + +```json + "App": { + "HealthCheck": { + } + } +```` diff --git a/Api.StartupTasks/icon.png b/Api.StartupTasks/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.StaticFiles/.docker/docker-compose.yml b/Api.StaticFiles/.docker/docker-compose.yml new file mode 100644 index 00000000..ceae04df --- /dev/null +++ b/Api.StaticFiles/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.staticfiles: + image: api.staticfiles + hostname: api-staticfiles + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.StaticFiles + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.StaticFiles/.dockerignore b/Api.StaticFiles/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.StaticFiles/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.StaticFiles/.github/config/slack.yml b/Api.StaticFiles/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.StaticFiles/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.StaticFiles/.github/workflows/build-and-deploy.yml b/Api.StaticFiles/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..cdc4dcb8 --- /dev/null +++ b/Api.StaticFiles/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.StaticFiles + IMAGE_NAME: api.staticfiles + SERVICE_NAME: api-staticfiles + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.StaticFiles/.gitignore b/Api.StaticFiles/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.StaticFiles/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.StaticFiles/.kubernetes/autoscaler.yaml b/Api.StaticFiles/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.StaticFiles/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.StaticFiles/.kubernetes/configmap.yaml b/Api.StaticFiles/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.StaticFiles/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.StaticFiles/.kubernetes/deployment.yaml b/Api.StaticFiles/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.StaticFiles/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.StaticFiles/.kubernetes/service.yaml b/Api.StaticFiles/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.StaticFiles/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Properties/DoNotParallelize.cs b/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Tests.Api.StaticFiles.csproj b/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Tests.Api.StaticFiles.csproj new file mode 100644 index 00000000..312152db --- /dev/null +++ b/Api.StaticFiles/.tests/Tests.Api.StaticFiles/Tests.Api.StaticFiles.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj b/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles.Models/Api.StaticFiles.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles.sln b/Api.StaticFiles/Api.StaticFiles.sln new file mode 100644 index 00000000..dd7885ad --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.StaticFiles.Models", "Api.StaticFiles.Models\Api.StaticFiles.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.StaticFiles", "Api.StaticFiles\Api.StaticFiles.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.StaticFiles", ".tests\Tests.Api.StaticFiles\Tests.Api.StaticFiles.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.StaticFiles/Api.StaticFiles/Api.StaticFiles.csproj b/Api.StaticFiles/Api.StaticFiles/Api.StaticFiles.csproj new file mode 100644 index 00000000..766773d6 --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/Api.StaticFiles.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/Controllers/ExamplesController.cs b/Api.StaticFiles/Api.StaticFiles/Controllers/ExamplesController.cs new file mode 100644 index 00000000..a6c62560 --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/Controllers/ExamplesController.cs @@ -0,0 +1,31 @@ +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; + +namespace Api.StaticFiles.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Static Files Action. + /// + /// The cancellation token. + /// A message. + /// Success. + [HttpGet] + [Route("static-files")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task StaticFilesAsync(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("static-files"); + } +} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local b/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local new file mode 100644 index 00000000..ad327791 --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.StaticFiles.dll"] \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/Program.cs b/Api.StaticFiles/Api.StaticFiles/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.StaticFiles/Api.StaticFiles/Properties/InternalsVisibleTo.cs b/Api.StaticFiles/Api.StaticFiles/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..f35cb962 --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.StaticFiles")] \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/appsettings.Development.json b/Api.StaticFiles/Api.StaticFiles/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/appsettings.Production.json b/Api.StaticFiles/Api.StaticFiles/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/appsettings.Staging.json b/Api.StaticFiles/Api.StaticFiles/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/appsettings.json b/Api.StaticFiles/Api.StaticFiles/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.StaticFiles/Api.StaticFiles/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.StaticFiles/Api.StaticFiles/wwwroot/fonts/open-sans-v44-latin-regular.woff2 b/Api.StaticFiles/Api.StaticFiles/wwwroot/fonts/open-sans-v44-latin-regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e2d3fa4ea21557e7ee10e08b42a82dd3de54ea4f GIT binary patch literal 18640 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

=pE2;YPyGP} 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%0DK88R_X{XRP@ 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 BR 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 WEaIVTB`mgga((~bV + + + + Static Files Test + + + + +

Static Files Test

+ Test Image +

If you see the image and text in the custom font, static files are working!

+ + \ No newline at end of file diff --git a/Api.StaticFiles/Dockerfile b/Api.StaticFiles/Dockerfile new file mode 100644 index 00000000..ab17eafa --- /dev/null +++ b/Api.StaticFiles/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.StaticFiles.dll"] \ No newline at end of file diff --git a/Api.StaticFiles/LICENSE b/Api.StaticFiles/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.StaticFiles/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.StaticFiles/README.md b/Api.StaticFiles/README.md new file mode 100644 index 00000000..aec8ceff --- /dev/null +++ b/Api.StaticFiles/README.md @@ -0,0 +1,31 @@ +# Api.StaticFiles + +> _Nano API application with static files._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +Static files have been added to the solution, including a font, an image, and an HTML file. +Run the application and navigate to [http://localhost:8080/index.html](http://localhost:8080/index.html) to verify that the static files are served correctly. + +The following endpoint is available for testing: + +| Endpoint | Description | +| -------------------------------------------------- | -------------------------------------- | +| `http://localhost:8080/api/examples/static-files` | Returns a simple `200 OK` response. | + +> 📖 Learn more about **[Nano Static Files](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#static-files)**. diff --git a/Api.StaticFiles/icon.png b/Api.StaticFiles/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~
+ + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Storage.Azure/.docker/docker-compose.yml b/Api.Storage.Azure/.docker/docker-compose.yml new file mode 100644 index 00000000..881edf06 --- /dev/null +++ b/Api.Storage.Azure/.docker/docker-compose.yml @@ -0,0 +1,19 @@ +services: + api.storage.azure: + image: api.storage.azure + hostname: api-storage-azure + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Storage.Azure + dockerfile: "Dockerfile.Local" + networks: + - network + volumes: + - ./bin/nano-storage-azure:/mnt/nano-storage-azure + +networks: + network: + name: network + driver: bridge diff --git a/Api.Storage.Azure/.dockerignore b/Api.Storage.Azure/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Storage.Azure/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Storage.Azure/.github/config/slack.yml b/Api.Storage.Azure/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Storage.Azure/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Storage.Azure/.github/workflows/build-and-deploy.yml b/Api.Storage.Azure/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..2b882124 --- /dev/null +++ b/Api.Storage.Azure/.github/workflows/build-and-deploy.yml @@ -0,0 +1,237 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Storage.Azure + IMAGE_NAME: api.storage.azure + SERVICE_NAME: api-storage-azure + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_STORAGE: ${{ vars.AZURE_STORAGE_RESOURCE_GROUP }} + AZURE_GROUP_BACKUP: ${{ vars.AZURE_BACKUP_RESOURCE_GROUP }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + STORAGE_SIZE: 1000 + STORAGE_SHARE_NAME: nano-storage-azure + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Create Fileshare + shell: pwsh + run: | + $env:STORAGE_ACCOUNT_NAME = az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].name -o tsv; + + $env:FILE_SHARE_EXISTS = az storage share-rm exists ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --query exists; + + if ($env:FILE_SHARE_EXISTS -eq "false") + { + az storage share-rm create ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --access-tier TransactionOptimized ` + --quota $env:STORAGE_SIZE; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $env:BACKUP_VAULT_NAME = az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; + + az backup protection enable-for-azurefileshare ` + -g $env:AZURE_GROUP_BACKUP ` + -v $env:BACKUP_VAULT_NAME ` + -p $env:STORAGE_ACCOUNT_NAME-backup-policy ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --azure-file-share $env:STORAGE_SHARE_NAME; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/storage-pv.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pv.tmp.yaml; + kubectl apply -f .kubernetes/storage-pv.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/storage-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pvc.tmp.yaml; + kubectl apply -f .kubernetes/storage-pvc.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Storage.Azure/.gitignore b/Api.Storage.Azure/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Storage.Azure/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Storage.Azure/.kubernetes/autoscaler.yaml b/Api.Storage.Azure/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Storage.Azure/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Storage.Azure/.kubernetes/configmap.yaml b/Api.Storage.Azure/.kubernetes/configmap.yaml new file mode 100644 index 00000000..b64a515d --- /dev/null +++ b/Api.Storage.Azure/.kubernetes/configmap.yaml @@ -0,0 +1,9 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + Storage__HealthCheck__AccountName: %STORAGE_ACCOUNT_NAME% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Storage.Azure/.kubernetes/deployment.yaml b/Api.Storage.Azure/.kubernetes/deployment.yaml new file mode 100644 index 00000000..9eb44e22 --- /dev/null +++ b/Api.Storage.Azure/.kubernetes/deployment.yaml @@ -0,0 +1,94 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + volumeMounts: + - name: tmp + mountPath: /tmp + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-azurefile-pvc + - name: tmp + emptyDir: {} + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Storage.Azure/.kubernetes/service.yaml b/Api.Storage.Azure/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Storage.Azure/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Storage.Azure/.kubernetes/storage-pv.yaml b/Api.Storage.Azure/.kubernetes/storage-pv.yaml new file mode 100644 index 00000000..b6281fb8 --- /dev/null +++ b/Api.Storage.Azure/.kubernetes/storage-pv.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: %SERVICE_NAME%-azurefile-pv +spec: + capacity: + storage: %STORAGE_SIZE% + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: azurefile-static + csi: + driver: file.csi.azure.com + volumeHandle: %STORAGE_SHARE_NAME% + volumeAttributes: + shareName: %STORAGE_SHARE_NAME% + storageAccount: %STORAGE_ACCOUNT_NAME% \ No newline at end of file diff --git a/Api.Storage.Azure/.kubernetes/storage-pvc.yaml b/Api.Storage.Azure/.kubernetes/storage-pvc.yaml new file mode 100644 index 00000000..252c7857 --- /dev/null +++ b/Api.Storage.Azure/.kubernetes/storage-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %SERVICE_NAME%-azurefile-pvc + namespace: %KUBERNETES_NAMESPACE% +spec: + accessModes: + - ReadWriteMany + storageClassName: azurefile-static + resources: + requests: + storage: %STORAGE_SIZE% \ No newline at end of file diff --git a/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Properties/DoNotParallelize.cs b/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Tests.Api.Storage.Azure.csproj b/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Tests.Api.Storage.Azure.csproj new file mode 100644 index 00000000..f15b810a --- /dev/null +++ b/Api.Storage.Azure/.tests/Tests.Api.Storage.Azure/Tests.Api.Storage.Azure.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj b/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj new file mode 100644 index 00000000..2865dfbb --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure.Models/Api.Storage.Azure.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure.sln b/Api.Storage.Azure/Api.Storage.Azure.sln new file mode 100644 index 00000000..a75a6568 --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure.sln @@ -0,0 +1,149 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + .kubernetes\storage-pv.yaml = .kubernetes\storage-pv.yaml + .kubernetes\storage-pvc.yaml = .kubernetes\storage-pvc.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Storage.Azure.Models", "Api.Storage.Azure.Models\Api.Storage.Azure.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Storage.Azure", "Api.Storage.Azure\Api.Storage.Azure.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Storage.Azure", ".tests\Tests.Api.Storage.Azure\Tests.Api.Storage.Azure.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Azure", "..\..\Nano.Library\Nano.Storage.Azure\Nano.Storage.Azure.csproj", "{5DD1307B-16FA-5171-54CA-4F37F2638022}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage", "..\..\Nano.Library\Nano.Storage\Nano.Storage.csproj", "{24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {5DD1307B-16FA-5171-54CA-4F37F2638022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5DD1307B-16FA-5171-54CA-4F37F2638022}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5DD1307B-16FA-5171-54CA-4F37F2638022}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5DD1307B-16FA-5171-54CA-4F37F2638022}.Release|Any CPU.Build.0 = Release|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5DD1307B-16FA-5171-54CA-4F37F2638022} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Storage.Azure/Api.Storage.Azure/Api.Storage.Azure.csproj b/Api.Storage.Azure/Api.Storage.Azure/Api.Storage.Azure.csproj new file mode 100644 index 00000000..a37eaf41 --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/Api.Storage.Azure.csproj @@ -0,0 +1,38 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + 91ada628-9aa0-450e-9029-98671550a47f + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/Controllers/ExamplesController.cs b/Api.Storage.Azure/Api.Storage.Azure/Controllers/ExamplesController.cs new file mode 100644 index 00000000..7fdfbdb7 --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/Controllers/ExamplesController.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Common.Consts; +using Nano.Storage.Abstractions; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Nano.App.Api.Extensions; + +namespace Api.Storage.Azure.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IPathProvider pathProvider) : BaseController(logger) +{ + /// + /// Storage Azure Action. + /// + /// The file. + /// The token used when request is cancelled. + /// A message. + /// OK. + [HttpPost] + [Route("storage")] + [Consumes(HttpContentType.FORM)] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task StorageAzureAsync([Required]IFormFile file, CancellationToken cancellationToken = default) + { + var fileName = Path.GetFileName(file.FileName); + var savePath = Path.Combine(pathProvider.Root, fileName); + + await file + .SaveFileAsync(savePath, cancellationToken); + + return this.Ok("storage-azure"); + } +} \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local b/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local new file mode 100644 index 00000000..eeb40cec --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Storage.Azure.dll"] \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/Program.cs b/Api.Storage.Azure/Api.Storage.Azure/Program.cs new file mode 100644 index 00000000..a6e84c1e --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Api; +using Nano.Storage.Azure; +using Nano.Storage.Extensions; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoStorage(); + }) + .Build() + .Run(); diff --git a/Api.Storage.Azure/Api.Storage.Azure/Properties/InternalsVisibleTo.cs b/Api.Storage.Azure/Api.Storage.Azure/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..cc3881c2 --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Storage.Azure")] \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Development.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Production.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.Staging.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Storage.Azure/Api.Storage.Azure/appsettings.json b/Api.Storage.Azure/Api.Storage.Azure/appsettings.json new file mode 100644 index 00000000..645e4243 --- /dev/null +++ b/Api.Storage.Azure/Api.Storage.Azure/appsettings.json @@ -0,0 +1,22 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + } + }, + "Storage": { + "ShareName": "nano-storage-azure", + "HealthCheck": { + "AccountName": null, + "UnhealthyStatus": "Degraded" + } + } +} \ No newline at end of file diff --git a/Api.Storage.Azure/Dockerfile b/Api.Storage.Azure/Dockerfile new file mode 100644 index 00000000..6b462057 --- /dev/null +++ b/Api.Storage.Azure/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Storage.Azure.dll"] \ No newline at end of file diff --git a/Api.Storage.Azure/LICENSE b/Api.Storage.Azure/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Storage.Azure/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Storage.Azure/README.md b/Api.Storage.Azure/README.md new file mode 100644 index 00000000..b4382605 --- /dev/null +++ b/Api.Storage.Azure/README.md @@ -0,0 +1,165 @@ +# Api.Storage.Azure + +> _Nano API application with azure storage._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This application demonstrates uploading a file and saving it to a mapped file share. +When running locally, files are **NOT** written to the Azure File Share. Instead, Docker mounts a local directory to simulate the file share. +Files are saved in `.docker/bin/`. + +A storage health check is configured to target the Azure File Share, but it requires valid credentials to be provided under `Storage.Credentials`. If the +credentials are omitted from the configuration, the application will still run, but the health-check will report `degraded`. + +Open [http://localhost:8080/healthz](http://localhost:8080/healthz) to view the storage health-check JSON response. + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#health-checks)**. + +The following endpoint is available for testing. + +| Endpoint | Description | +| --------------------------------------------------- | ------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/storage` | Returns a simple `200 OK` response. Saves the uploaded file to the fileshare. | + +> 📖 Learn more about **[Nano.Storage.Azure](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Storage.Azure/README#nanostorageazure)**. + +## Registration +The following storage provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(x => +{ + x.AddNanoStorage(); +}) +... +``` + +## Configuration +Add the storage configuration. + +```json +"Storage": { + "ShareName": "nano-storage-azure", + "HealthCheck": { + "AccountName": null, + "UnhealthyStatus": "Degraded" + } +} +``` + +Additionally, application health-checks have been enabled with the configuration. + +```json +"App": { + "HealthCheck": { + } +} +``` + +## Docker Compose +Mapped the fileshare in `docker-compose.yml`. + +```yaml +docker + volumes: + - ./bin/nano-storage-azure:/mnt/nano-storage-azure +``` + +## Kubernetes +Added two new kubernetes templaets, the `storage-pv.yaml` and `storage-pvc.yaml`. Updated the `deployment.yaml` mounting the volume. + +```yaml +spec: + template: + spec: + containers: + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-azurefile-pvc + - name: tmp + emptyDir: {} +``` + +Additionally, the `configmap.yaml` file stores the `$env:STORAGE_ACCOUNT_NAME` value, which is used by the TCP-based health check to validate connectivity to the Azure File Share endpoint. + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + AZURE_GROUP_BACKUP: ${{ vars.AZURE_BACKUP_RESOURCE_GROUP }} + AZURE_GROUP_STORAGE: ${{ vars.AZURE_STORAGE_RESOURCE_GROUP }} + STORAGE_SIZE: 1000 + STORAGE_SHARE_NAME: nano-storage-azure +``` + +And add this step below as well, ensuring that the fileshare gets created before the application is deployed. + +```yaml +- name: Create Fileshare + shell: pwsh + run: | + $env:STORAGE_ACCOUNT_NAME = sudo az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].name -o tsv; + + $env:FILE_SHARE_EXISTS = sudo az storage share-rm exists ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --query exists; + + if ($env:FILE_SHARE_EXISTS -eq "false") + { + sudo az storage share-rm create ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --access-tier TransactionOptimized ` + --quota $env:STORAGE_SIZE; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $env:BACKUP_VAULT_NAME = sudo az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; + + sudo az backup protection enable-for-azurefileshare ` + -g $env:AZURE_GROUP_BACKUP ` + -v $env:BACKUP_VAULT_NAME ` + -p $env:STORAGE_ACCOUNT_NAME-backup-policy ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --azure-file-share $env:STORAGE_SHARE_NAME; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } +``` \ No newline at end of file diff --git a/Api.Storage.Azure/icon.png b/Api.Storage.Azure/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Storage.Local/.docker/docker-compose.yml b/Api.Storage.Local/.docker/docker-compose.yml new file mode 100644 index 00000000..c33c1940 --- /dev/null +++ b/Api.Storage.Local/.docker/docker-compose.yml @@ -0,0 +1,19 @@ +services: + api.storage.local: + image: api.storage.local + hostname: api-storage-local + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Storage.Local + dockerfile: "Dockerfile.Local" + networks: + - network + volumes: + - ./bin/nano-storage-local:/mnt/nano-storage-local + +networks: + network: + name: network + driver: bridge diff --git a/Api.Storage.Local/.dockerignore b/Api.Storage.Local/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Storage.Local/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Storage.Local/.github/config/slack.yml b/Api.Storage.Local/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Storage.Local/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Storage.Local/.github/workflows/build-and-deploy.yml b/Api.Storage.Local/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..3044ba11 --- /dev/null +++ b/Api.Storage.Local/.github/workflows/build-and-deploy.yml @@ -0,0 +1,194 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Storage.Local + IMAGE_NAME: api.storage.local + SERVICE_NAME: api-storage-local + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + STORAGE_SIZE: 1000 + STORAGE_SHARE_NAME: nano-storage-local + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/storage-storageclass.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-storageclass.tmp.yaml; + kubectl apply -f .kubernetes/storage-storageclass.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/storage-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pvc.tmp.yaml; + kubectl apply -f .kubernetes/storage-pvc.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Storage.Local/.gitignore b/Api.Storage.Local/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Storage.Local/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Storage.Local/.kubernetes/autoscaler.yaml b/Api.Storage.Local/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Storage.Local/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Storage.Local/.kubernetes/configmap.yaml b/Api.Storage.Local/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Storage.Local/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Storage.Local/.kubernetes/deployment.yaml b/Api.Storage.Local/.kubernetes/deployment.yaml new file mode 100644 index 00000000..eb1c9093 --- /dev/null +++ b/Api.Storage.Local/.kubernetes/deployment.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc + - name: tmp + emptyDir: {} + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Storage.Local/.kubernetes/service.yaml b/Api.Storage.Local/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Storage.Local/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Storage.Local/.kubernetes/storage-pvc.yaml b/Api.Storage.Local/.kubernetes/storage-pvc.yaml new file mode 100644 index 00000000..ca5fdd40 --- /dev/null +++ b/Api.Storage.Local/.kubernetes/storage-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %SERVICE_NAME%-pvc + namespace: %KUBERNETES_NAMESPACE% +spec: + accessModes: + - ReadWriteOnce + storageClassName: %SERVICE_NAME%-storage-class + resources: + requests: + storage: %STORAGE_SIZE% \ No newline at end of file diff --git a/Api.Storage.Local/.kubernetes/storage-storageclass.yaml b/Api.Storage.Local/.kubernetes/storage-storageclass.yaml new file mode 100644 index 00000000..f9ccaa3e --- /dev/null +++ b/Api.Storage.Local/.kubernetes/storage-storageclass.yaml @@ -0,0 +1,10 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: %SERVICE_NAME%-storage-class +provisioner: kubernetes.io/azure-disk +parameters: + storageaccounttype: Standard_LRS + kind: Managed +reclaimPolicy: Retain +volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Properties/DoNotParallelize.cs b/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Tests.Api.Storage.Local.csproj b/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Tests.Api.Storage.Local.csproj new file mode 100644 index 00000000..535b3efb --- /dev/null +++ b/Api.Storage.Local/.tests/Tests.Api.Storage.Local/Tests.Api.Storage.Local.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj b/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj new file mode 100644 index 00000000..8f47aa23 --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local.Models/Api.Storage.Local.Models.csproj @@ -0,0 +1,77 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local.sln b/Api.Storage.Local/Api.Storage.Local.sln new file mode 100644 index 00000000..b4d234bc --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local.sln @@ -0,0 +1,149 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + .kubernetes\storage-pvc.yaml = .kubernetes\storage-pvc.yaml + .kubernetes\storage-storageclass.yaml = .kubernetes\storage-storageclass.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Storage.Local.Models", "Api.Storage.Local.Models\Api.Storage.Local.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Storage.Local", "Api.Storage.Local\Api.Storage.Local.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Storage.Local", ".tests\Tests.Api.Storage.Local\Tests.Api.Storage.Local.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage", "..\..\Nano.Library\Nano.Storage\Nano.Storage.csproj", "{24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Local", "..\..\Nano.Library\Nano.Storage.Local\Nano.Storage.Local.csproj", "{6D21CF50-D3ED-4D55-27D9-D426E661A5E4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.Build.0 = Release|Any CPU + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Storage.Local/Api.Storage.Local/Api.Storage.Local.csproj b/Api.Storage.Local/Api.Storage.Local/Api.Storage.Local.csproj new file mode 100644 index 00000000..d892da66 --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/Api.Storage.Local.csproj @@ -0,0 +1,38 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + 9c0012fe-f61b-4cda-b051-a7219073bc53 + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/Controllers/ExamplesController.cs b/Api.Storage.Local/Api.Storage.Local/Controllers/ExamplesController.cs new file mode 100644 index 00000000..870442da --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/Controllers/ExamplesController.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.App.Api.Extensions; +using Nano.Common.Consts; +using Nano.Storage.Abstractions; + +namespace Api.Storage.Local.Controllers; + +/// +/// Controller with examples. +/// +/// The . +/// The . +public class ExamplesController(ILogger logger, IPathProvider pathProvider) : BaseController(logger) +{ + /// + /// Storage Local. + /// + /// The file. + /// The token used when request is cancelled. + /// A message. + /// OK. + [HttpPost] + [Route("storage")] + [Consumes(HttpContentType.FORM)] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task StorageLocalAsync([Required]IFormFile file, CancellationToken cancellationToken = default) + { + var fileName = Path.GetFileName(file.FileName); + var savePath = Path.Combine(pathProvider.Root, fileName); + + await file + .SaveFileAsync(savePath, cancellationToken); + + return this.Ok("storage-local"); + } +} \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local b/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local new file mode 100644 index 00000000..6512ea12 --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Storage.Local.dll"] \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/Program.cs b/Api.Storage.Local/Api.Storage.Local/Program.cs new file mode 100644 index 00000000..5116914b --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Api; +using Nano.Storage.Extensions; +using Nano.Storage.Local; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(x => + { + x.AddNanoStorage(); + }) + .Build() + .Run(); diff --git a/Api.Storage.Local/Api.Storage.Local/Properties/InternalsVisibleTo.cs b/Api.Storage.Local/Api.Storage.Local/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..e4169829 --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Storage.Local")] \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.Development.json b/Api.Storage.Local/Api.Storage.Local/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.Production.json b/Api.Storage.Local/Api.Storage.Local/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.Staging.json b/Api.Storage.Local/Api.Storage.Local/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Storage.Local/Api.Storage.Local/appsettings.json b/Api.Storage.Local/Api.Storage.Local/appsettings.json new file mode 100644 index 00000000..6a2c9c46 --- /dev/null +++ b/Api.Storage.Local/Api.Storage.Local/appsettings.json @@ -0,0 +1,21 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "HealthCheck": { + } + }, + "Storage": { + "ShareName": "nano-storage-local", + "HealthCheck": { + "UnhealthyStatus": "UnHealthy" + } + } +} \ No newline at end of file diff --git a/Api.Storage.Local/Dockerfile b/Api.Storage.Local/Dockerfile new file mode 100644 index 00000000..657921b0 --- /dev/null +++ b/Api.Storage.Local/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Storage.Local.dll"] \ No newline at end of file diff --git a/Api.Storage.Local/LICENSE b/Api.Storage.Local/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Storage.Local/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Storage.Local/README.md b/Api.Storage.Local/README.md new file mode 100644 index 00000000..c243734f --- /dev/null +++ b/Api.Storage.Local/README.md @@ -0,0 +1,123 @@ +# Api.Storage.Local + +> _Nano API application with local storage._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This application demonstrates uploading a file and saving it to a locally mapped fileshare. +Files are saved in `.docker/bin/`. + +Storage health-check has also been configured. +Open [http://localhost:8080/healthz](http://localhost:8080/healthz) to view the storage health-check in the JSON response. + +The following endpoint is available for testing: + +| Endpoint | Description | +| --------------------------------------------- | ------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/storage` | Returns a simple `200 OK` response. Saves the uploaded file to the fileshare. | + +> 📖 Learn more about **[Nano.Storage.Local](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Storage.Local/README#nanostoragelocal)**. + +## Registration +The following storage has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(x => +{ + x.AddNanoStorage(); +}) +... +``` + +## Configuration +Configured the application with the necessary storage setup. + +```json +"Storage": { + "ShareName": "nano-storage-local", + "HealthCheck": { + "UnhealthyStatus": "Degraded" + } +} +``` + +Additionally, application health-checks have been enabled with the configuration. + +```json +"App": { + "HealthCheck": { + } +} +``` + +## Docker Compose +Mapped the fileshare in `docker-compose.yml`. + +```yaml +docker + volumes: + - ./bin/nano-storage-local:/mnt/nano-storage-local +``` + +## Kubernetes +Added two additional kubernetes templates, `storage-storageclass.yaml` and `storage-pvc.yaml`, for dynamically manage and creating the local fileshare. + +Also, updated `deployment.yaml` adding the volumes and volume mounts. + +```yaml +spec: + template: + spec: + containers: + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc + - name: tmp + emptyDir: {} +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + STORAGE_SHARE_NAME: nano-storage-local + STORAGE_SIZE: 1000 +``` + +Deployment commands have also been updated to apply each of the new Kubernetes templates. + +```powershell +Get-Content .kubernetes/{resource-name}.yaml ` + | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` + | Set-Content .kubernetes/{resource-name}.tmp.yaml; + +sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; +``` diff --git a/Api.Storage.Local/icon.png b/Api.Storage.Local/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~ + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.TimeZone/.docker/docker-compose.yml b/Api.TimeZone/.docker/docker-compose.yml new file mode 100644 index 00000000..f9debe0d --- /dev/null +++ b/Api.TimeZone/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.timezone: + image: api.timezone + hostname: api-timezone + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.TimeZone + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.TimeZone/.dockerignore b/Api.TimeZone/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.TimeZone/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.TimeZone/.github/config/slack.yml b/Api.TimeZone/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.TimeZone/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.TimeZone/.github/workflows/build-and-deploy.yml b/Api.TimeZone/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..edbde0d8 --- /dev/null +++ b/Api.TimeZone/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.TimeZone + IMAGE_NAME: api.timezone + SERVICE_NAME: api-timezone + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.TimeZone/.gitignore b/Api.TimeZone/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.TimeZone/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.TimeZone/.kubernetes/autoscaler.yaml b/Api.TimeZone/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.TimeZone/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.TimeZone/.kubernetes/configmap.yaml b/Api.TimeZone/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.TimeZone/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.TimeZone/.kubernetes/deployment.yaml b/Api.TimeZone/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.TimeZone/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.TimeZone/.kubernetes/service.yaml b/Api.TimeZone/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.TimeZone/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.TimeZone/.tests/Tests.Api.TimeZone/Properties/DoNotParallelize.cs b/Api.TimeZone/.tests/Tests.Api.TimeZone/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.TimeZone/.tests/Tests.Api.TimeZone/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.TimeZone/.tests/Tests.Api.TimeZone/Tests.Api.TimeZone.csproj b/Api.TimeZone/.tests/Tests.Api.TimeZone/Tests.Api.TimeZone.csproj new file mode 100644 index 00000000..f3c341ee --- /dev/null +++ b/Api.TimeZone/.tests/Tests.Api.TimeZone/Tests.Api.TimeZone.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj b/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.TimeZone/Api.TimeZone.Models/Api.TimeZone.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone.sln b/Api.TimeZone/Api.TimeZone.sln new file mode 100644 index 00000000..8ba99efb --- /dev/null +++ b/Api.TimeZone/Api.TimeZone.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.TimeZone.Models", "Api.TimeZone.Models\Api.TimeZone.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.TimeZone", "Api.TimeZone\Api.TimeZone.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.TimeZone", ".tests\Tests.Api.TimeZone\Tests.Api.TimeZone.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj b/Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj new file mode 100644 index 00000000..36aee63d --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/Api.TimeZone.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs b/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs new file mode 100644 index 00000000..f41edbdb --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/Controllers/ExamplesController.cs @@ -0,0 +1,71 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Api.TimeZone.Controllers.Requests; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Vivet.AspNetCore.RequestTimeZone; + +namespace Api.TimeZone.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Time Zone GET Action. + /// + /// The date time. + /// The cancellation token. + /// An object showing different date times. + /// Success. + [HttpGet] + [Route("timezone")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task TimeZoneAsync([FromQuery][Required]DateTimeOffset dateTime, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + var clientRequestDateTime = TimeZoneInfo.ConvertTime(dateTime, DateTimeInfo.TimeZone.Value!); + + return this.Ok(new + { + RequestDateTimeLocal = clientRequestDateTime, + ServerRecievedUtc = dateTime.UtcDateTime, + ResponseDateTimeLocal = dateTime, + DateTimeInfoNow = DateTimeInfo.Now, + DateTimeInfoUtcNow = DateTimeInfo.UtcNow.UtcDateTime + }); + } + + /// + /// Time Zone POST Action. + /// + /// The date time request. + /// The cancellation token. + /// An object showing different date times. + /// Success. + [HttpPost] + [Route("timezone")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task TimeZoneAsync([FromBody][Required]DateTimeRequest request, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + var clientRequestDateTime = TimeZoneInfo.ConvertTime(request.DateTime, DateTimeInfo.TimeZone.Value!); + + return this.Ok(new + { + RequestDateTimeLocal = clientRequestDateTime, + ServerRecievedUtc = request.DateTime.UtcDateTime, + ResponseDateTimeLocal = request.DateTime, + DateTimeInfoNow = DateTimeInfo.Now, + DateTimeInfoUtcNow = DateTimeInfo.UtcNow.UtcDateTime + }); + } +} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Controllers/Requests/DateTimeRequest.cs b/Api.TimeZone/Api.TimeZone/Controllers/Requests/DateTimeRequest.cs new file mode 100644 index 00000000..b5f7b723 --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/Controllers/Requests/DateTimeRequest.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Api.TimeZone.Controllers.Requests; + +/// +/// Date Time Request. +/// +public class DateTimeRequest +{ + /// + /// Date Time. + /// + [Required] + public virtual DateTimeOffset DateTime { get; set; } +} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Dockerfile.Local b/Api.TimeZone/Api.TimeZone/Dockerfile.Local new file mode 100644 index 00000000..0ab8570b --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.TimeZone.dll"] \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/Program.cs b/Api.TimeZone/Api.TimeZone/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.TimeZone/Api.TimeZone/Properties/InternalsVisibleTo.cs b/Api.TimeZone/Api.TimeZone/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..d871e5fc --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.TimeZone")] \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/appsettings.Development.json b/Api.TimeZone/Api.TimeZone/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/appsettings.Production.json b/Api.TimeZone/Api.TimeZone/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/appsettings.Staging.json b/Api.TimeZone/Api.TimeZone/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.TimeZone/Api.TimeZone/appsettings.json b/Api.TimeZone/Api.TimeZone/appsettings.json new file mode 100644 index 00000000..9026e1cb --- /dev/null +++ b/Api.TimeZone/Api.TimeZone/appsettings.json @@ -0,0 +1,16 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + }, + "TimeZone": { + "DefaultTimeZone": "UTC" + } + } +} \ No newline at end of file diff --git a/Api.TimeZone/Dockerfile b/Api.TimeZone/Dockerfile new file mode 100644 index 00000000..f7c7188e --- /dev/null +++ b/Api.TimeZone/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.TimeZone.dll"] \ No newline at end of file diff --git a/Api.TimeZone/LICENSE b/Api.TimeZone/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.TimeZone/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.TimeZone/README.md b/Api.TimeZone/README.md new file mode 100644 index 00000000..70e6d283 --- /dev/null +++ b/Api.TimeZone/README.md @@ -0,0 +1,52 @@ +# Api.TimeZone + +> _Nano API application with request timezone._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +The example demonstrates using request time zone in a Nano application. + +Both the GET and POST endpoints in the controller return a response in the following format: + +```json +{ + "RequestDateTimeLocal": "2026-02-08T14:23:45+03:00", // The date-time sent by the client in the request + "ServerReceivedUtc": "2026-02-08T11:23:45Z", // The UTC date-time when the server received the request + "ResponseDateTimeLocal": "2026-02-08T14:23:45+03:00", // The date-time returned to the client, adjusted to the requested timezone + "DateTimeInfoNow": "2026-02-10T11:40:37+03:00", // The current local date-time on the server + "DateTimeInfoUtcNow": "2026-02-10T08:40:37.7265135Z" // The current UTC date-time on the server +} +``` + +The following endpoint is available for testing. + +| Endpoint | Description | +| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `http://localhost:8080/api/examples/timezone` (GET,POST) | Returns a `200 OK` response with various `DateTimeOffset` properties illustrating the date-time timezone conversions. | + +> 📖 Learn more about **[Nano TimeZone](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#timezone)**. + +## Configuration +```json +"App": { + "TimeZone": { + "DefaultTimeZone": "UTC" + } +} +``` diff --git a/Api.TimeZone/icon.png b/Api.TimeZone/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.Versioning/.docker/docker-compose.yml b/Api.Versioning/.docker/docker-compose.yml new file mode 100644 index 00000000..c3a3fc80 --- /dev/null +++ b/Api.Versioning/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.versioning: + image: api.versioning + hostname: api-versioning + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Versioning + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.Versioning/.dockerignore b/Api.Versioning/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.Versioning/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.Versioning/.github/config/slack.yml b/Api.Versioning/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.Versioning/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.Versioning/.github/workflows/build-and-deploy.yml b/Api.Versioning/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..3a01b8e4 --- /dev/null +++ b/Api.Versioning/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Versioning + IMAGE_NAME: api.versioning + SERVICE_NAME: api-versioning + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.Versioning/.gitignore b/Api.Versioning/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.Versioning/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.Versioning/.kubernetes/autoscaler.yaml b/Api.Versioning/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.Versioning/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.Versioning/.kubernetes/configmap.yaml b/Api.Versioning/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.Versioning/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.Versioning/.kubernetes/deployment.yaml b/Api.Versioning/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.Versioning/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.Versioning/.kubernetes/service.yaml b/Api.Versioning/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.Versioning/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.Versioning/.tests/Tests.Api.Versioning/Properties/DoNotParallelize.cs b/Api.Versioning/.tests/Tests.Api.Versioning/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.Versioning/.tests/Tests.Api.Versioning/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.Versioning/.tests/Tests.Api.Versioning/Tests.Api.Versioning.csproj b/Api.Versioning/.tests/Tests.Api.Versioning/Tests.Api.Versioning.csproj new file mode 100644 index 00000000..f652aec3 --- /dev/null +++ b/Api.Versioning/.tests/Tests.Api.Versioning/Tests.Api.Versioning.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj b/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.Versioning/Api.Versioning.Models/Api.Versioning.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning.sln b/Api.Versioning/Api.Versioning.sln new file mode 100644 index 00000000..1e775d1c --- /dev/null +++ b/Api.Versioning/Api.Versioning.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Versioning.Models", "Api.Versioning.Models\Api.Versioning.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Versioning", "Api.Versioning\Api.Versioning.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Versioning", ".tests\Tests.Api.Versioning\Tests.Api.Versioning.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.Versioning/Api.Versioning/Api.Versioning.csproj b/Api.Versioning/Api.Versioning/Api.Versioning.csproj new file mode 100644 index 00000000..ef6130b1 --- /dev/null +++ b/Api.Versioning/Api.Versioning/Api.Versioning.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/Controllers/ExamplesController.cs b/Api.Versioning/Api.Versioning/Controllers/ExamplesController.cs new file mode 100644 index 00000000..4308de4f --- /dev/null +++ b/Api.Versioning/Api.Versioning/Controllers/ExamplesController.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Asp.Versioning; + +namespace Api.Versioning.Controllers; + +/// +/// Controller with examples. +/// +/// The . +[ApiVersion("1.0")] +[ApiVersion("2.0")] +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Version 1.0 (default) Action. + /// + /// The cancellation token. + /// A message. + /// Ok. + [HttpGet] + [Route("versioning")] + [MapToApiVersion("1.0")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task Version10Async(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("v1.0"); + } + + /// + /// Version 2.0 Action. + /// + /// The cancellation token. + /// A message. + /// Ok. + [HttpGet] + [Route("versioning")] + [MapToApiVersion("2.0")] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task Version20Async(CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("v2.0"); + } +} \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/Dockerfile.Local b/Api.Versioning/Api.Versioning/Dockerfile.Local new file mode 100644 index 00000000..273713a4 --- /dev/null +++ b/Api.Versioning/Api.Versioning/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Versioning.dll"] \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/Program.cs b/Api.Versioning/Api.Versioning/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.Versioning/Api.Versioning/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.Versioning/Api.Versioning/Properties/InternalsVisibleTo.cs b/Api.Versioning/Api.Versioning/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..75c29478 --- /dev/null +++ b/Api.Versioning/Api.Versioning/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Versioning")] \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/appsettings.Development.json b/Api.Versioning/Api.Versioning/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Versioning/Api.Versioning/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/appsettings.Production.json b/Api.Versioning/Api.Versioning/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Versioning/Api.Versioning/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/appsettings.Staging.json b/Api.Versioning/Api.Versioning/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.Versioning/Api.Versioning/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.Versioning/Api.Versioning/appsettings.json b/Api.Versioning/Api.Versioning/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api.Versioning/Api.Versioning/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api.Versioning/Dockerfile b/Api.Versioning/Dockerfile new file mode 100644 index 00000000..079b95c9 --- /dev/null +++ b/Api.Versioning/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Versioning.dll"] \ No newline at end of file diff --git a/Api.Versioning/LICENSE b/Api.Versioning/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.Versioning/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.Versioning/README.md b/Api.Versioning/README.md new file mode 100644 index 00000000..82f8fd69 --- /dev/null +++ b/Api.Versioning/README.md @@ -0,0 +1,35 @@ +# Api.Versioning + +> _Nano API application with versioning._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +The controller is annotated with `[ApiVersion("1.0")]` and `[ApiVersion("2.0")]`, and contains two actions, each mapped to a specific version +using `[MapToApiVersion("1.0")]` and `[MapToApiVersion("2.0")]`, respectively. + +Observe the response headers `Api-Supported-Version`, containing all versions supported by the application, and the `Api-Version` showing the requested API version +by the client, or the default version if omitted from the request. + +The following endpoint is available for testing. + +| Endpoint | Description | +| ----------------------------------------------------- | ----------------------------------------------------- | +| `http://localhost:8080/api/v1.0/examples/versioning` | Returns a `200 OK` response with the message `v1.0`. | +| `http://localhost:8080/api/v2.0/examples/versioning` | Returns a `200 OK` response with the message `v2.0`. | + +> 📖 Learn more about **[Nano Versioning](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#versioning)**. diff --git a/Api.Versioning/icon.png b/Api.Versioning/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api.VirusScan/.docker/docker-compose.yml b/Api.VirusScan/.docker/docker-compose.yml new file mode 100644 index 00000000..0d5b74ed --- /dev/null +++ b/Api.VirusScan/.docker/docker-compose.yml @@ -0,0 +1,27 @@ +services: + api.virusscan: + image: api.virusscan + hostname: api-virusscan + restart: on-failure + ports: + - 8080:8080 + depends_on: + - clamav + build: + context: ../Api.VirusScan + dockerfile: "Dockerfile.Local" + networks: + - network + + clamav: + image: clamav/clamav + hostname: clamav + ports: + - 3310:3310 + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api.VirusScan/.dockerignore b/Api.VirusScan/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api.VirusScan/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api.VirusScan/.github/config/slack.yml b/Api.VirusScan/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api.VirusScan/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api.VirusScan/.github/workflows/build-and-deploy.yml b/Api.VirusScan/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..0e0eb79a --- /dev/null +++ b/Api.VirusScan/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.VirusScan + IMAGE_NAME: api.virusscan + SERVICE_NAME: api-virusscan + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api.VirusScan/.gitignore b/Api.VirusScan/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api.VirusScan/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api.VirusScan/.kubernetes/autoscaler.yaml b/Api.VirusScan/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api.VirusScan/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api.VirusScan/.kubernetes/configmap.yaml b/Api.VirusScan/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api.VirusScan/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api.VirusScan/.kubernetes/deployment.yaml b/Api.VirusScan/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Api.VirusScan/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api.VirusScan/.kubernetes/service.yaml b/Api.VirusScan/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api.VirusScan/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api.VirusScan/.tests/Tests.Api.VirusScan/Properties/DoNotParallelize.cs b/Api.VirusScan/.tests/Tests.Api.VirusScan/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api.VirusScan/.tests/Tests.Api.VirusScan/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api.VirusScan/.tests/Tests.Api.VirusScan/Tests.Api.VirusScan.csproj b/Api.VirusScan/.tests/Tests.Api.VirusScan/Tests.Api.VirusScan.csproj new file mode 100644 index 00000000..bc2d2085 --- /dev/null +++ b/Api.VirusScan/.tests/Tests.Api.VirusScan/Tests.Api.VirusScan.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj b/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api.VirusScan/Api.VirusScan.Models/Api.VirusScan.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan.sln b/Api.VirusScan/Api.VirusScan.sln new file mode 100644 index 00000000..6eacc515 --- /dev/null +++ b/Api.VirusScan/Api.VirusScan.sln @@ -0,0 +1,140 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.VirusScan.Models", "Api.VirusScan.Models\Api.VirusScan.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.VirusScan", "Api.VirusScan\Api.VirusScan.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.VirusScan", ".tests\Tests.Api.VirusScan\Tests.Api.VirusScan.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Vivet.AspNetCore.RequestVirusScan", "..\..\..\Vivet\Vivet.AspNetCore\Vivet.AspNetCore.RequestVirusScan\Vivet.AspNetCore.RequestVirusScan.csproj", "{04A79939-6413-2CB5-D3FA-974BBBCD8F7B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {04A79939-6413-2CB5-D3FA-974BBBCD8F7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04A79939-6413-2CB5-D3FA-974BBBCD8F7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04A79939-6413-2CB5-D3FA-974BBBCD8F7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04A79939-6413-2CB5-D3FA-974BBBCD8F7B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {04A79939-6413-2CB5-D3FA-974BBBCD8F7B} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api.VirusScan/Api.VirusScan/Api.VirusScan.csproj b/Api.VirusScan/Api.VirusScan/Api.VirusScan.csproj new file mode 100644 index 00000000..2046e5df --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/Api.VirusScan.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/Controllers/ExamplesController.cs b/Api.VirusScan/Api.VirusScan/Controllers/ExamplesController.cs new file mode 100644 index 00000000..02094acc --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/Controllers/ExamplesController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Nano.App.Api.Controllers; +using Nano.Common.Consts; +using System.ComponentModel.DataAnnotations; +using System.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Api.VirusScan.Controllers; + +/// +/// Controller with examples. +/// +/// The . +public class ExamplesController(ILogger logger) : BaseController(logger) +{ + /// + /// Virus Scan Action. + /// + /// The file. + /// The token used when request is cancelled. + /// A message. + /// OK. + [HttpPost] + [Route("virus-scan")] + [Consumes(HttpContentType.FORM)] + [ProducesResponseType((int)HttpStatusCode.OK)] + public virtual async Task VirusScanAsync([Required]IFormFile file, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + return this.Ok("no virus"); + } +} \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/Dockerfile.Local b/Api.VirusScan/Api.VirusScan/Dockerfile.Local new file mode 100644 index 00000000..bcc73a22 --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.VirusScan.dll"] \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/Program.cs b/Api.VirusScan/Api.VirusScan/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api.VirusScan/Api.VirusScan/Properties/InternalsVisibleTo.cs b/Api.VirusScan/Api.VirusScan/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..663ae613 --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.VirusScan")] \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/appsettings.Development.json b/Api.VirusScan/Api.VirusScan/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/appsettings.Production.json b/Api.VirusScan/Api.VirusScan/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/appsettings.Staging.json b/Api.VirusScan/Api.VirusScan/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api.VirusScan/Api.VirusScan/appsettings.json b/Api.VirusScan/Api.VirusScan/appsettings.json new file mode 100644 index 00000000..e3e6bc45 --- /dev/null +++ b/Api.VirusScan/Api.VirusScan/appsettings.json @@ -0,0 +1,22 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + }, + }, + "HealthCheck": { + }, + "VirusScan": { + "Host": "clamav", + "Port": 3310, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } + } +} \ No newline at end of file diff --git a/Api.VirusScan/Dockerfile b/Api.VirusScan/Dockerfile new file mode 100644 index 00000000..6fde6c7c --- /dev/null +++ b/Api.VirusScan/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.VirusScan.dll"] \ No newline at end of file diff --git a/Api.VirusScan/LICENSE b/Api.VirusScan/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api.VirusScan/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api.VirusScan/README.md b/Api.VirusScan/README.md new file mode 100644 index 00000000..520b6ee9 --- /dev/null +++ b/Api.VirusScan/README.md @@ -0,0 +1,71 @@ +# Api.VirusScan + +> _Nano API application with virus scanning._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the **[Nano.Library](https://github.com/Nano-Core/Nano.Library)** repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +> 💡 Explore API requests for this lesson in our **[Public Nano Workspace on Postman](https://www.postman.com/nanocore/nano-lessons)**. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) + +## Summary +This application builds on **[Api.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Api._Blank)** and adds a simple test controller +that inherits from the top-level Nano `BaseController`. + +This example demonstrates using virus scan for all uploaded files in a Nano application. + +An virus scan health check is configured. +Open **[http://localhost:8080/healthz](http://localhost:8080/healthz)** to view the health-check status in the JSON response. + +> 📖 Learn more about **[Nano Health Checks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#health-checks)** + +Invoke the endpoint. To test virus detection, you can use the EICAR test files +available here: [https://www.eicar.org/download-anti-malware-testfile](https://www.eicar.org/download-anti-malware-testfile). + +The following endpoint is available for testing. + +| Endpoint | Description | +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | +| `http://localhost:8080/api/examples/virus-scan` | Returns a `200 OK` response if there is no virus in the file, and otherwise a `500 ERROR` with the found virus name in the error message. | + +> 📖 Learn more about **[Nano Virus Scan](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#virus-scan)**. + +## Configuration +```json +"App": { + "VirusScan": { + "Host": "clamav", + "Port": 3310, + "HealthCheck": { + "UnhealthyStatus": "Unhealthy" + } + } +} +``` + +## Docker Compose +Added `ClamAV` dependency to the `docker-compose.yml` + +```yaml +services: + api.virusscan: + depends_on: + - clamav + + clamav: + image: clamav/clamav + hostname: clamav + ports: + - 3310:3310 + networks: + - network +``` diff --git a/Api.VirusScan/icon.png b/Api.VirusScan/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Api._Blank/.docker/docker-compose.yml b/Api._Blank/.docker/docker-compose.yml new file mode 100644 index 00000000..a4aabf90 --- /dev/null +++ b/Api._Blank/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + api.blank: + image: api.blank + hostname: api-blank + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Api.Blank + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Api._Blank/.dockerignore b/Api._Blank/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Api._Blank/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Api._Blank/.github/config/slack.yml b/Api._Blank/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Api._Blank/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Api._Blank/.github/workflows/build-and-deploy.yml b/Api._Blank/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..11902a3d --- /dev/null +++ b/Api._Blank/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Api.Blank + IMAGE_NAME: api.blank + SERVICE_NAME: api-blank + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Api._Blank/.gitignore b/Api._Blank/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Api._Blank/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Api._Blank/.kubernetes/autoscaler.yaml b/Api._Blank/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Api._Blank/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Api._Blank/.kubernetes/configmap.yaml b/Api._Blank/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Api._Blank/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Api._Blank/.kubernetes/deployment.yaml b/Api._Blank/.kubernetes/deployment.yaml new file mode 100644 index 00000000..b15e7e29 --- /dev/null +++ b/Api._Blank/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Api._Blank/.kubernetes/service.yaml b/Api._Blank/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Api._Blank/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Api._Blank/.tests/Tests.Api.Blank/Properties/DoNotParallelize.cs b/Api._Blank/.tests/Tests.Api.Blank/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Api._Blank/.tests/Tests.Api.Blank/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Api._Blank/.tests/Tests.Api.Blank/Tests.Api.Blank.csproj b/Api._Blank/.tests/Tests.Api.Blank/Tests.Api.Blank.csproj new file mode 100644 index 00000000..100b3cf2 --- /dev/null +++ b/Api._Blank/.tests/Tests.Api.Blank/Tests.Api.Blank.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj b/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj new file mode 100644 index 00000000..c33a8e9a --- /dev/null +++ b/Api._Blank/Api.Blank.Models/Api.Blank.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Api._Blank/Api.Blank.sln b/Api._Blank/Api.Blank.sln new file mode 100644 index 00000000..37172573 --- /dev/null +++ b/Api._Blank/Api.Blank.sln @@ -0,0 +1,133 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Blank.Models", "Api.Blank.Models\Api.Blank.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Api.Blank", "Api.Blank\Api.Blank.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Api.Blank", ".tests\Tests.Api.Blank\Tests.Api.Blank.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Api._Blank/Api.Blank/Api.Blank.csproj b/Api._Blank/Api.Blank/Api.Blank.csproj new file mode 100644 index 00000000..638806ab --- /dev/null +++ b/Api._Blank/Api.Blank/Api.Blank.csproj @@ -0,0 +1,37 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Api._Blank/Api.Blank/Dockerfile.Local b/Api._Blank/Api.Blank/Dockerfile.Local new file mode 100644 index 00000000..9c84943b --- /dev/null +++ b/Api._Blank/Api.Blank/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Api.Blank.dll"] \ No newline at end of file diff --git a/Api._Blank/Api.Blank/Program.cs b/Api._Blank/Api.Blank/Program.cs new file mode 100644 index 00000000..32865375 --- /dev/null +++ b/Api._Blank/Api.Blank/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Api; + +NanoApiApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); diff --git a/Api._Blank/Api.Blank/Properties/InternalsVisibleTo.cs b/Api._Blank/Api.Blank/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..b99fa9a8 --- /dev/null +++ b/Api._Blank/Api.Blank/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Api.Blank")] \ No newline at end of file diff --git a/Api._Blank/Api.Blank/appsettings.Development.json b/Api._Blank/Api.Blank/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api._Blank/Api.Blank/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api._Blank/Api.Blank/appsettings.Production.json b/Api._Blank/Api.Blank/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api._Blank/Api.Blank/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api._Blank/Api.Blank/appsettings.Staging.json b/Api._Blank/Api.Blank/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Api._Blank/Api.Blank/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Api._Blank/Api.Blank/appsettings.json b/Api._Blank/Api.Blank/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Api._Blank/Api.Blank/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Api._Blank/Dockerfile b/Api._Blank/Dockerfile new file mode 100644 index 00000000..c06b6792 --- /dev/null +++ b/Api._Blank/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Api.Blank.dll"] \ No newline at end of file diff --git a/Api._Blank/LICENSE b/Api._Blank/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Api._Blank/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Api._Blank/README.md b/Api._Blank/README.md new file mode 100644 index 00000000..caafa49d --- /dev/null +++ b/Api._Blank/README.md @@ -0,0 +1,26 @@ +# Api.Blank + +> _Minimal (blank) Nano API application._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application represents the most minimal (blank) Nano API application setup. + +Its purpose is to demonstrate the required boilerplate, file structure, and general configuration needed to create a Nano API application. +The application itself is intentionally minimal and does not expose HTTP endpoints or configure additional features. Instead, it serves as a baseline +from which all other API lessons and examples are built. + +It is recommended to review this application first to understand how a Nano API application is generally structured and to become familiar with the purpose of the core building blocks +used in the boilerplate. + +> 📖 Learn more about **[Nano Solution Composition](https://github.com/Nano-Core/Nano.Library#solution-composition)**. diff --git a/Api._Blank/icon.png b/Api._Blank/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 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 + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.CustomConfigSection/.docker/docker-compose.yml b/Console.CustomConfigSection/.docker/docker-compose.yml new file mode 100644 index 00000000..f055dd68 --- /dev/null +++ b/Console.CustomConfigSection/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.customconfigsection: + image: console.customconfigsection + restart: on-failure + build: + context: ../Console.CustomConfigSection + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.CustomConfigSection/.dockerignore b/Console.CustomConfigSection/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.CustomConfigSection/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.CustomConfigSection/.github/config/slack.yml b/Console.CustomConfigSection/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.CustomConfigSection/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml b/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..930c99ac --- /dev/null +++ b/Console.CustomConfigSection/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.CustomConfigSection + IMAGE_NAME: console.customconfigsection + SERVICE_NAME: console-customconfigsection + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.CustomConfigSection/.gitignore b/Console.CustomConfigSection/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.CustomConfigSection/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.CustomConfigSection/.kubernetes/configmap.yaml b/Console.CustomConfigSection/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.CustomConfigSection/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.CustomConfigSection/.kubernetes/cronjob.yaml b/Console.CustomConfigSection/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.CustomConfigSection/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Properties/DoNotParallelize.cs b/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Tests.Console.CustomConfigSection.csproj b/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Tests.Console.CustomConfigSection.csproj new file mode 100644 index 00000000..352e3917 --- /dev/null +++ b/Console.CustomConfigSection/.tests/Tests.Console.CustomConfigSection/Tests.Console.CustomConfigSection.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.CustomConfigSection/Console.CustomConfigSection.sln b/Console.CustomConfigSection/Console.CustomConfigSection.sln new file mode 100644 index 00000000..d0a81346 --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 d18.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.CustomConfigSection", "Console.CustomConfigSection\Console.CustomConfigSection.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.CustomConfigSection", ".tests\Tests.Console.CustomConfigSection\Tests.Console.CustomConfigSection.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Config/CustomOptions.cs b/Console.CustomConfigSection/Console.CustomConfigSection/Config/CustomOptions.cs new file mode 100644 index 00000000..4ecca56e --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/Config/CustomOptions.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; + +namespace Console.CustomConfigSection.Config; + +/// +/// Custom Options. +/// +public class CustomOptions +{ + internal static string SectionName => "Custom"; + + /// + /// Value. + /// + [Required] + public virtual string Value { get; set; } = null!; +} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Console.CustomConfigSection.csproj b/Console.CustomConfigSection/Console.CustomConfigSection/Console.CustomConfigSection.csproj new file mode 100644 index 00000000..e0dfc0c1 --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/Console.CustomConfigSection.csproj @@ -0,0 +1,43 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Dockerfile.Local b/Console.CustomConfigSection/Console.CustomConfigSection/Dockerfile.Local new file mode 100644 index 00000000..f80be018 --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Program.cs b/Console.CustomConfigSection/Console.CustomConfigSection/Program.cs new file mode 100644 index 00000000..1acc2bdd --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/Program.cs @@ -0,0 +1,12 @@ +using Console.CustomConfigSection.Config; +using Nano.App.Console; +using Nano.Common.Config.Extensions; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoConfigSection(CustomOptions.SectionName, out _); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Properties/InternalsVisibleTo.cs b/Console.CustomConfigSection/Console.CustomConfigSection/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..58489958 --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.CustomConfigSection")] \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/Workers/ExampleWorker.cs b/Console.CustomConfigSection/Console.CustomConfigSection/Workers/ExampleWorker.cs new file mode 100644 index 00000000..146982d8 --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/Workers/ExampleWorker.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Console.CustomConfigSection.Config; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Nano.App.Console.Workers; + +namespace Console.CustomConfigSection.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IOptionsMonitor customOptions) : BaseWorker(logger) +{ + private readonly IOptionsMonitor customOptions = customOptions ?? throw new ArgumentNullException(nameof(customOptions)); + + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.CompletedTask; + + System.Console.WriteLine($"Custom Config Section Value: '{this.customOptions.CurrentValue.Value}'"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Development.json b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Production.json b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Staging.json b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.json b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.json new file mode 100644 index 00000000..a0d63f9c --- /dev/null +++ b/Console.CustomConfigSection/Console.CustomConfigSection/appsettings.json @@ -0,0 +1,8 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Custom": { + "Value": "custom-value" + } +} \ No newline at end of file diff --git a/Console.CustomConfigSection/Dockerfile b/Console.CustomConfigSection/Dockerfile new file mode 100644 index 00000000..14f95f28 --- /dev/null +++ b/Console.CustomConfigSection/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.CustomConfigSection.dll"] \ No newline at end of file diff --git a/Console.CustomConfigSection/README.md b/Console.CustomConfigSection/README.md new file mode 100644 index 00000000..f1f29df6 --- /dev/null +++ b/Console.CustomConfigSection/README.md @@ -0,0 +1,59 @@ +# Console.CustomConfigSection + +> _Nano Console application with custom configuration section._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Configuration](#configuration) +* [Registration](#registration) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates a worker that uses a custom configuration section. + +Run the application to see services and hosted services being resolved, and observe the console output generated by the `ExampleWorker` +and the injected `CustomOptions` implementation. + +> 📖 Learn more about **[Nano Custom Configuration Sections](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Api/README.md#custom-configuration-section)**. + +## Configuration +A custom configuration section has been added to `appsettings.json`: + +```json +"Custom": { + "Value": "custom-value" +} +``` + +## Registration +An option class matching the structure of the configuration has been implemented. + +```csharp +public class CustomOptions +{ + internal static string SectionName => "Custom"; + + [Required] + public virtual string Value { get; set; } = null!; +} +``` + +Finally, the options model has been registered with the section in startup, as shown below. + +```csharp +... +.ConfigureServices(x => +{ + x.AddNanoConfigSection(CustomOptions.SectionName, out _); +}) +... +``` diff --git a/Console.CustomService/.docker/docker-compose.dcproj b/Console.CustomService/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.CustomService/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.CustomService/.docker/docker-compose.yml b/Console.CustomService/.docker/docker-compose.yml new file mode 100644 index 00000000..e05f251f --- /dev/null +++ b/Console.CustomService/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.customservice: + image: console.customservice + restart: on-failure + build: + context: ../Console.CustomService + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.CustomService/.dockerignore b/Console.CustomService/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.CustomService/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.CustomService/.github/config/slack.yml b/Console.CustomService/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.CustomService/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.CustomService/.github/workflows/build-and-deploy.yml b/Console.CustomService/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..784ed671 --- /dev/null +++ b/Console.CustomService/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.CustomService + IMAGE_NAME: console.customservice + SERVICE_NAME: console-customservice + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.CustomService/.gitignore b/Console.CustomService/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.CustomService/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.CustomService/.kubernetes/configmap.yaml b/Console.CustomService/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.CustomService/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.CustomService/.kubernetes/cronjob.yaml b/Console.CustomService/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.CustomService/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.CustomService/.tests/Tests.Console.CustomService/Properties/DoNotParallelize.cs b/Console.CustomService/.tests/Tests.Console.CustomService/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.CustomService/.tests/Tests.Console.CustomService/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.CustomService/.tests/Tests.Console.CustomService/Tests.Console.CustomService.csproj b/Console.CustomService/.tests/Tests.Console.CustomService/Tests.Console.CustomService.csproj new file mode 100644 index 00000000..5912cf8a --- /dev/null +++ b/Console.CustomService/.tests/Tests.Console.CustomService/Tests.Console.CustomService.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.CustomService/Console.CustomService.sln b/Console.CustomService/Console.CustomService.sln new file mode 100644 index 00000000..b4bb6e17 --- /dev/null +++ b/Console.CustomService/Console.CustomService.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 d18.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.CustomService", "Console.CustomService\Console.CustomService.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.CustomService", ".tests\Tests.Console.CustomService\Tests.Console.CustomService.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.CustomService/Console.CustomService/Console.CustomService.csproj b/Console.CustomService/Console.CustomService/Console.CustomService.csproj new file mode 100644 index 00000000..e0dfc0c1 --- /dev/null +++ b/Console.CustomService/Console.CustomService/Console.CustomService.csproj @@ -0,0 +1,43 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Dockerfile.Local b/Console.CustomService/Console.CustomService/Dockerfile.Local new file mode 100644 index 00000000..0e1d9813 --- /dev/null +++ b/Console.CustomService/Console.CustomService/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.CustomService.dll"] \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Program.cs b/Console.CustomService/Console.CustomService/Program.cs new file mode 100644 index 00000000..ba27d399 --- /dev/null +++ b/Console.CustomService/Console.CustomService/Program.cs @@ -0,0 +1,13 @@ +using Console.CustomService.Services; +using Console.CustomService.Services.Abstractions; +using Microsoft.Extensions.DependencyInjection; +using Nano.App.Console; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddScoped(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Properties/InternalsVisibleTo.cs b/Console.CustomService/Console.CustomService/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..ac8322c5 --- /dev/null +++ b/Console.CustomService/Console.CustomService/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.CustomService")] \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Services/Abstractions/IExampleService.cs b/Console.CustomService/Console.CustomService/Services/Abstractions/IExampleService.cs new file mode 100644 index 00000000..cf1fd568 --- /dev/null +++ b/Console.CustomService/Console.CustomService/Services/Abstractions/IExampleService.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; + +namespace Console.CustomService.Services.Abstractions; + +/// +/// Example Service Interface. +/// +public interface IExampleService +{ + /// + /// Get Message. + /// + /// A message. + Task GetMessage(); +} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Services/ExampleService.cs b/Console.CustomService/Console.CustomService/Services/ExampleService.cs new file mode 100644 index 00000000..e1c81720 --- /dev/null +++ b/Console.CustomService/Console.CustomService/Services/ExampleService.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using Console.CustomService.Services.Abstractions; + +namespace Console.CustomService.Services; + +/// +/// Example Service. +/// +public class ExampleService : IExampleService +{ + /// + public Task GetMessage() + { + return Task.FromResult("Message from example service."); + } +} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/Workers/ExampleWorker.cs b/Console.CustomService/Console.CustomService/Workers/ExampleWorker.cs new file mode 100644 index 00000000..f601ad46 --- /dev/null +++ b/Console.CustomService/Console.CustomService/Workers/ExampleWorker.cs @@ -0,0 +1,35 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Console.CustomService.Services.Abstractions; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; + +namespace Console.CustomService.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IExampleService exampleService) : BaseWorker(logger) +{ + private readonly IExampleService exampleService = exampleService ?? throw new ArgumentNullException(nameof(exampleService)); + + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + var message = await this.exampleService + .GetMessage(); + + System.Console.WriteLine(message); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/appsettings.Development.json b/Console.CustomService/Console.CustomService/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.CustomService/Console.CustomService/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/appsettings.Production.json b/Console.CustomService/Console.CustomService/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.CustomService/Console.CustomService/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/appsettings.Staging.json b/Console.CustomService/Console.CustomService/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.CustomService/Console.CustomService/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.CustomService/Console.CustomService/appsettings.json b/Console.CustomService/Console.CustomService/appsettings.json new file mode 100644 index 00000000..c7b251f2 --- /dev/null +++ b/Console.CustomService/Console.CustomService/appsettings.json @@ -0,0 +1,5 @@ +{ + "App": { + "Version": "1.0.0.0" + } +} \ No newline at end of file diff --git a/Console.CustomService/Dockerfile b/Console.CustomService/Dockerfile new file mode 100644 index 00000000..0486f4ec --- /dev/null +++ b/Console.CustomService/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.CustomService.dll"] \ No newline at end of file diff --git a/Console.CustomService/README.md b/Console.CustomService/README.md new file mode 100644 index 00000000..4bc057a7 --- /dev/null +++ b/Console.CustomService/README.md @@ -0,0 +1,38 @@ +# Console.CustomService + +> _Nano Console application with custom scoped service._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates a worker that use a custom service. + +Run the application to see services and hosted services being resolved, and observe the console output generated by the `ExampleWorker` +and the injected `IExampleService` implementation. + +> 📖 Learn more about **[Nano Custom Services](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App/README.md#custom-services)**. + +## Registration +A custom service, `IExampleService` has been added and implemented. In `program.cs` the service is registered using `ConfigureService(...)` method as shown below + +```csharp +... +.ConfigureServices(services => +{ + services + .AddScoped(); +}) +... +``` diff --git a/Console.Data.InMemory/.docker/docker-compose.dcproj b/Console.Data.InMemory/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Data.InMemory/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Data.InMemory/.docker/docker-compose.yml b/Console.Data.InMemory/.docker/docker-compose.yml new file mode 100644 index 00000000..e72d1145 --- /dev/null +++ b/Console.Data.InMemory/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.data.inmemory: + image: console.data.inmemory + restart: on-failure + build: + context: ../Console.Data.InMemory + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.Data.InMemory/.dockerignore b/Console.Data.InMemory/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Data.InMemory/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Data.InMemory/.github/config/slack.yml b/Console.Data.InMemory/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Data.InMemory/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.InMemory/.github/workflows/build-and-deploy.yml b/Console.Data.InMemory/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..9733b697 --- /dev/null +++ b/Console.Data.InMemory/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Data.InMemory + IMAGE_NAME: console.data.inmemory + SERVICE_NAME: console-data-inmemory + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.InMemory/.gitignore b/Console.Data.InMemory/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Data.InMemory/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.InMemory/.kubernetes/configmap.yaml b/Console.Data.InMemory/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Data.InMemory/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.InMemory/.kubernetes/cronjob.yaml b/Console.Data.InMemory/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..71ff564c --- /dev/null +++ b/Console.Data.InMemory/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret diff --git a/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Properties/DoNotParallelize.cs b/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Tests.Console.Data.InMemory.csproj b/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Tests.Console.Data.InMemory.csproj new file mode 100644 index 00000000..f7243f3c --- /dev/null +++ b/Console.Data.InMemory/.tests/Tests.Console.Data.InMemory/Tests.Console.Data.InMemory.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Data.InMemory/Console.Data.InMemory.sln b/Console.Data.InMemory/Console.Data.InMemory.sln new file mode 100644 index 00000000..30427fbb --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory.sln @@ -0,0 +1,137 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.InMemory", "Console.Data.InMemory\Console.Data.InMemory.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.InMemory", ".tests\Tests.Console.Data.InMemory\Tests.Console.Data.InMemory.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.InMemory", "..\..\Nano.Library\Nano.Data.InMemory\Nano.Data.InMemory.csproj", "{C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C39E1045-E7D8-4F2D-B1CE-C7BAA59D6EF6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Data.InMemory/Console.Data.InMemory/Console.Data.InMemory.csproj b/Console.Data.InMemory/Console.Data.InMemory/Console.Data.InMemory.csproj new file mode 100644 index 00000000..d4f9f974 --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/Console.Data.InMemory.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Data/InMemoryDbContext.cs b/Console.Data.InMemory/Console.Data.InMemory/Data/InMemoryDbContext.cs new file mode 100644 index 00000000..4f1337b4 --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/Data/InMemoryDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Console.Data.InMemory.Data; + +/// +public class InMemoryDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Data/Mappings/ExampleMapping.cs b/Console.Data.InMemory/Console.Data.InMemory/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..ef3fdeac --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Console.Data.InMemory.Data.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Console.Data.InMemory.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Data/Models/Example.cs b/Console.Data.InMemory/Console.Data.InMemory/Data/Models/Example.cs new file mode 100644 index 00000000..2d5ae3a7 --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/Data/Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Console.Data.InMemory.Data.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Dockerfile.Local b/Console.Data.InMemory/Console.Data.InMemory/Dockerfile.Local new file mode 100644 index 00000000..a1eafcb3 --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Data.InMemory.dll"] \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Program.cs b/Console.Data.InMemory/Console.Data.InMemory/Program.cs new file mode 100644 index 00000000..14cf320d --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/Program.cs @@ -0,0 +1,13 @@ +using Console.Data.InMemory.Data; +using Nano.App.Console; +using Nano.Data.Extensions; +using Nano.Data.InMemory; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Properties/InternalsVisibleTo.cs b/Console.Data.InMemory/Console.Data.InMemory/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..dd0d85af --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Data.InMemory")] \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/Workers/ExampleWorker.cs b/Console.Data.InMemory/Console.Data.InMemory/Workers/ExampleWorker.cs new file mode 100644 index 00000000..a495b5a2 --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/Workers/ExampleWorker.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Console.Data.InMemory.Data.Models; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using Nano.Data.Abstractions; + +namespace Console.Data.InMemory.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) +{ + private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); + + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + var example = await this.repository + .AddAsync(new Example + { + Name = "name" + }, cancellationToken); + + await this.repository + .SaveChangesAsync(cancellationToken); + + example = await this.repository + .GetAsync(example.Id, cancellationToken); + + System.Console.WriteLine($"The example name is: '{example!.Name}'"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Development.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Production.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.Staging.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.InMemory/Console.Data.InMemory/appsettings.json b/Console.Data.InMemory/Console.Data.InMemory/appsettings.json new file mode 100644 index 00000000..254689c8 --- /dev/null +++ b/Console.Data.InMemory/Console.Data.InMemory/appsettings.json @@ -0,0 +1,27 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "UseLazyLoading": false, + "UseCreateDatabase": false, + "UseMigrateDatabase": false, + "UseSoftDeletetion": false, + "UseSensitiveDataLogging": false, + "UseAudit": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": "nanoDb", + "Repository": { + "UseAutoSave": false, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null + } +} \ No newline at end of file diff --git a/Console.Data.InMemory/Dockerfile b/Console.Data.InMemory/Dockerfile new file mode 100644 index 00000000..9a0ba936 --- /dev/null +++ b/Console.Data.InMemory/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Data.InMemory.dll"] \ No newline at end of file diff --git a/Console.Data.InMemory/README.md b/Console.Data.InMemory/README.md new file mode 100644 index 00000000..d6cb69d4 --- /dev/null +++ b/Console.Data.InMemory/README.md @@ -0,0 +1,71 @@ +# Console.Data.InMemory + +> _Nano API application with in-memory data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings)**, +and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context)**. + +Notice, that the `InMemoryProvider` doesn't require any implementation of `BaseDbContextFactory` and there is no need to add migrations. + +The worker creates and retreives a `Example` entity using the Nano **[Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories)**. Run the +application and observe the console output generated by the `ExampleWorker`. + +> 📖 Learn more about **[Nano.Data.InMemory](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.InMemory/README#nanodatainmemory)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "UseLazyLoading": false, + "UseCreateDatabase": false, + "UseMigrateDatabase": false, + "UseSoftDeletetion": false, + "UseSensitiveDataLogging": false, + "UseAudit": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": "nanoDb", + "Repository": { + "UseAutoSave": false, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null +} +``` diff --git a/Console.Data.MySql/.docker/docker-compose.dcproj b/Console.Data.MySql/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Data.MySql/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Data.MySql/.docker/docker-compose.yml b/Console.Data.MySql/.docker/docker-compose.yml new file mode 100644 index 00000000..3a849720 --- /dev/null +++ b/Console.Data.MySql/.docker/docker-compose.yml @@ -0,0 +1,29 @@ +services: + console.data.mysql: + image: console.data.mysql + restart: on-failure + build: + context: ../Console.Data.MySql + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' + +networks: + network: + name: network + external: true diff --git a/Console.Data.MySql/.dockerignore b/Console.Data.MySql/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Data.MySql/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Data.MySql/.github/config/slack.yml b/Console.Data.MySql/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Data.MySql/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.MySql/.github/workflows/build-and-deploy.yml b/Console.Data.MySql/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..7e45a90a --- /dev/null +++ b/Console.Data.MySql/.github/workflows/build-and-deploy.yml @@ -0,0 +1,207 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Data.MySql + IMAGE_NAME: console.data.mysql + SERVICE_NAME: console-data-mysql + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName -o tsv; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort -o tsv; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $userExists = mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');"; + + if ($userExists -eq 0) + { + mysql ` + --host $env:SQL_HOST ` + --port $env:SQL_PORT ` + --user $env:SQL_ADMIN_USER ` + --ssl-mode=REQUIRED ` + -e "CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; GRANT SELECT, INSERT, UPDATE, DELETE ON $env:SQL_NAME.* TO '$env:SQL_USER'@'%'; FLUSH PRIVILEGES;"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/sql-auth-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/sql-auth-secret.tmp.yaml; + kubectl apply -f .kubernetes/sql-auth-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.MySql/.gitignore b/Console.Data.MySql/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Data.MySql/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.MySql/.kubernetes/auth-sql-secret.yaml b/Console.Data.MySql/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Console.Data.MySql/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Console.Data.MySql/.kubernetes/configmap.yaml b/Console.Data.MySql/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Data.MySql/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.MySql/.kubernetes/cronjob.yaml b/Console.Data.MySql/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..9ae388c3 --- /dev/null +++ b/Console.Data.MySql/.kubernetes/cronjob.yaml @@ -0,0 +1,57 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret diff --git a/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Properties/DoNotParallelize.cs b/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Tests.Console.Data.MySql.csproj b/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Tests.Console.Data.MySql.csproj new file mode 100644 index 00000000..fadd2d23 --- /dev/null +++ b/Console.Data.MySql/.tests/Tests.Console.Data.MySql/Tests.Console.Data.MySql.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Data.MySql/Console.Data.MySql.sln b/Console.Data.MySql/Console.Data.MySql.sln new file mode 100644 index 00000000..7a66ecdc --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql.sln @@ -0,0 +1,138 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.MySql", "Console.Data.MySql\Console.Data.MySql.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.MySql", ".tests\Tests.Console.Data.MySql\Tests.Console.Data.MySql.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.MySql", "..\..\Nano.Library\Nano.Data.MySql\Nano.Data.MySql.csproj", "{E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {E7DC90EE-C82A-5CC7-8F33-9EC2FEC36291} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Data.MySql/Console.Data.MySql/Console.Data.MySql.csproj b/Console.Data.MySql/Console.Data.MySql/Console.Data.MySql.csproj new file mode 100644 index 00000000..ecb2bb5e --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Console.Data.MySql.csproj @@ -0,0 +1,49 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Data/Mappings/ExampleMapping.cs b/Console.Data.MySql/Console.Data.MySql/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..0b8d424b --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Console.Data.MySql.Data.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Console.Data.MySql.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Data/Models/Example.cs b/Console.Data.MySql/Console.Data.MySql/Data/Models/Example.cs new file mode 100644 index 00000000..28ea9e14 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Data/Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Console.Data.MySql.Data.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContext.cs b/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContext.cs new file mode 100644 index 00000000..1aede2a6 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Console.Data.MySql.Data; + +/// +public class MySqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContextFactory.cs b/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContextFactory.cs new file mode 100644 index 00000000..7985d145 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Data/MySqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.MySql; + +namespace Console.Data.MySql.Data; + +/// +public class MySqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Dockerfile.Local b/Console.Data.MySql/Console.Data.MySql/Dockerfile.Local new file mode 100644 index 00000000..2b8d2330 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Data.MySql.dll"] \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Migrations/20260425173327_Initial.Designer.cs b/Console.Data.MySql/Console.Data.MySql/Migrations/20260425173327_Initial.Designer.cs new file mode 100644 index 00000000..e7296d5e --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Migrations/20260425173327_Initial.Designer.cs @@ -0,0 +1,651 @@ +// +using System; +using Console.Data.MySql.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Console.Data.MySql.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + [Migration("20260425173327_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Console.Data.MySql.Data.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Console.Data.MySql/Console.Data.MySql/Migrations/20260425173327_Initial.cs b/Console.Data.MySql/Console.Data.MySql/Migrations/20260425173327_Initial.cs new file mode 100644 index 00000000..e6bbd48f --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Migrations/20260425173327_Initial.cs @@ -0,0 +1,601 @@ +using System; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Console.Data.MySql.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + CreatedBy = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityKey = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + EntitySetName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityTypeName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + FriendlyName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Xml = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IsActive = table.Column(type: "tinyint(1)", nullable: false, defaultValue: true), + UserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedUserName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + Email = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NormalizedEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + EmailConfirmed = table.Column(type: "tinyint(1)", nullable: false), + PasswordHash = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + SecurityStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ConcurrencyStamp = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumber = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + PhoneNumberConfirmed = table.Column(type: "tinyint(1)", nullable: false), + TwoFactorEnabled = table.Column(type: "tinyint(1)", nullable: false), + LockoutEnd = table.Column(type: "datetime(6)", nullable: true), + LockoutEnabled = table.Column(type: "tinyint(1)", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ParentId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + PropertyName = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + RelationName = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + OldValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Name = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Hash = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + RevokedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + NewEmail = table.Column(type: "varchar(256)", maxLength: 256, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + NewPhoneNumber = table.Column(type: "varchar(20)", maxLength: 20, nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderKey = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ProviderDisplayName = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4"), + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + IdentityUserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + AppId = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "varchar(256)", maxLength: 256, nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ExpireAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + LoginProvider = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Value = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ClaimType = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + ClaimValue = table.Column(type: "longtext", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + ApiKeyId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + RoleId = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci") + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Console.Data.MySql/Console.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs b/Console.Data.MySql/Console.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..3dc5ef24 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Migrations/MySqlDbContextModelSnapshot.cs @@ -0,0 +1,648 @@ +// +using System; +using Console.Data.MySql.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Console.Data.MySql.Migrations +{ + [DbContext(typeof(MySqlDbContext))] + partial class MySqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Console.Data.MySql.Data.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("longtext"); + + b.Property("Xml") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityKey") + .HasColumnType("char(36)"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("longtext"); + + b.Property("OldValue") + .HasColumnType("longtext"); + + b.Property("ParentId") + .HasColumnType("char(36)"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ApiKeyId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("varchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("varchar(255)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetime(6)"); + + b.Property("IdentityUserId") + .HasColumnType("char(36)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Console.Data.MySql/Console.Data.MySql/Program.cs b/Console.Data.MySql/Console.Data.MySql/Program.cs new file mode 100644 index 00000000..b81a098d --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Program.cs @@ -0,0 +1,13 @@ +using Console.Data.MySql.Data; +using Nano.App.Console; +using Nano.Data.Extensions; +using Nano.Data.MySql; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Properties/InternalsVisibleTo.cs b/Console.Data.MySql/Console.Data.MySql/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..132b71d7 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Data.MySql")] \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/Workers/ExampleWorker.cs b/Console.Data.MySql/Console.Data.MySql/Workers/ExampleWorker.cs new file mode 100644 index 00000000..dda41519 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/Workers/ExampleWorker.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Console.Data.MySql.Data.Models; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using Nano.Data.Abstractions; + +namespace Console.Data.MySql.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) +{ + private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); + + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + var example = await this.repository + .AddAsync(new Example + { + Name = "name" + }, cancellationToken); + + await this.repository + .SaveChangesAsync(cancellationToken); + + example = await this.repository + .GetAsync(example.Id, cancellationToken); + + System.Console.WriteLine($"The example name is: '{example!.Name}'"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json b/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json new file mode 100644 index 00000000..34379ce9 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" + } +} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.Production.json b/Console.Data.MySql/Console.Data.MySql/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.Staging.json b/Console.Data.MySql/Console.Data.MySql/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.MySql/Console.Data.MySql/appsettings.json b/Console.Data.MySql/Console.Data.MySql/appsettings.json new file mode 100644 index 00000000..87c218f8 --- /dev/null +++ b/Console.Data.MySql/Console.Data.MySql/appsettings.json @@ -0,0 +1,24 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null + } +} \ No newline at end of file diff --git a/Console.Data.MySql/Dockerfile b/Console.Data.MySql/Dockerfile new file mode 100644 index 00000000..db692014 --- /dev/null +++ b/Console.Data.MySql/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Data.MySql.dll"] \ No newline at end of file diff --git a/Console.Data.MySql/README.md b/Console.Data.MySql/README.md new file mode 100644 index 00000000..7d5948c4 --- /dev/null +++ b/Console.Data.MySql/README.md @@ -0,0 +1,174 @@ +# Console.Data.MySql + +> _Nano API application with mysql data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings)**, +and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context)**. + +The worker creates and retreives a `Example` entity using the Nano **[Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories)**. Run the +application and observe the console output generated by the `ExampleWorker`. + +> 📖 Learn more about **[Nano.Data.MySql](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.MySql/README.md#nanodatamysql)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +Also, an initial migration has been added to the project. + +```powershell +dotnet ef migrations add Initial --project Console.Data.MySql +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null +} +``` + +...and `appsettings.Development.json` + +```json +"Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal;Database=nanoDb;Uid=sa;Pwd=myPassword_123" +} +``` + +## Docker Compose +Added MySql as a service dependency in `docker-compose.yml`. + +```yaml +services: + console.data.mysql: + depends_on: + - database + + database: + image: mysql/mysql-server:latest + ports: + - 3306:3306 + networks: + - network + environment: + MYSQL_USER: sa + MYSQL_PASSWORD: myPassword_123 + MYSQL_ROOT_PASSWORD: myPassword_123 + MYSQL_DATABASE: nanoDb + MYSQL_ROOT_HOST: '%' +``` + +## Kubernetes +Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. + +```json +spec: + jobTemplate: + spec: + template: + spec: + containers: + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +``` + +Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. + +```yaml +- name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].fullyQualifiedDomainName; + $env:SQL_PORT = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].databasePort; + $env:SQL_ADMIN_USER = az mysql flexible-server list -g $env:AZURE_GROUP_DATABASE --query [0].administratorLogin; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_ADMIN_USER;Pwd=$env:SQL_ADMIN_PASSWORD;SslMode=Preferred"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING "; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update; + apt-get install -y mysql-client; + + $userExists = mysql --batch -e "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '$env:SQL_USER');" $env:SQL_MIGRATION_CONNECTIONSTRING; + + if ($userExists -eq 0) + { + mysql --connect-expired-password -e " ` + CREATE USER '$env:SQL_USER'@'%' IDENTIFIED BY '$env:SQL_PASSWORD'; ` + GRANT SELECT, INSERT, UPDATE, DELETE ON $database.* TO '$env:SQL_USER'@'%'; ` + FLUSH PRIVILEGES;" $env:SQL_MIGRATION_CONNECTIONSTRING; + } +``` + +Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. + diff --git a/Console.Data.PostgreSQL/.docker/docker-compose.dcproj b/Console.Data.PostgreSQL/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Data.PostgreSQL/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Data.PostgreSQL/.docker/docker-compose.yml b/Console.Data.PostgreSQL/.docker/docker-compose.yml new file mode 100644 index 00000000..c3f9dac1 --- /dev/null +++ b/Console.Data.PostgreSQL/.docker/docker-compose.yml @@ -0,0 +1,27 @@ +services: + console.data.postgresql: + image: console.data.postgresql + restart: on-failure + build: + context: ../Console.Data.PostgreSQL + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: postgis/postgis:latest + ports: + - 5432:5432 + networks: + - network + environment: + POSTGRES_USER: sa + POSTGRES_PASSWORD: myPassword_123 + POSTGRES_DB: nanoDb + +networks: + network: + name: network + external: true diff --git a/Console.Data.PostgreSQL/.dockerignore b/Console.Data.PostgreSQL/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Data.PostgreSQL/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Data.PostgreSQL/.github/config/slack.yml b/Console.Data.PostgreSQL/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Data.PostgreSQL/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml b/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..8e9abbbf --- /dev/null +++ b/Console.Data.PostgreSQL/.github/workflows/build-and-deploy.yml @@ -0,0 +1,215 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Data.PostgreSQL + IMAGE_NAME: console.data.postgresql + SERVICE_NAME: console-data-postgresql + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-mysql-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "5432"; + $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y postgresql-client + + $userExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "CREATE ROLE $env:SQL_USER WITH LOGIN PASSWORD '$env:SQL_PASSWORD';" + } + + $userDbExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userDbExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT CONNECT ON DATABASE $env:SQL_NAME TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT USAGE ON SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:SQL_USER;" + } + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Uid=$env:SQL_USER;Pwd=$env:SQL_PASSWORD;SslMode=Preferred"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.PostgreSQL/.gitignore b/Console.Data.PostgreSQL/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Data.PostgreSQL/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml b/Console.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Console.Data.PostgreSQL/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Console.Data.PostgreSQL/.kubernetes/configmap.yaml b/Console.Data.PostgreSQL/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Data.PostgreSQL/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml b/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..9ae388c3 --- /dev/null +++ b/Console.Data.PostgreSQL/.kubernetes/cronjob.yaml @@ -0,0 +1,57 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret diff --git a/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Properties/DoNotParallelize.cs b/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Tests.Console.Data.PostgreSQL.csproj b/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Tests.Console.Data.PostgreSQL.csproj new file mode 100644 index 00000000..2be29a09 --- /dev/null +++ b/Console.Data.PostgreSQL/.tests/Tests.Console.Data.PostgreSQL/Tests.Console.Data.PostgreSQL.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL.sln b/Console.Data.PostgreSQL/Console.Data.PostgreSQL.sln new file mode 100644 index 00000000..78fe9f28 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL.sln @@ -0,0 +1,138 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.PostgreSQL", "Console.Data.PostgreSQL\Console.Data.PostgreSQL.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.PostgreSQL", ".tests\Tests.Console.Data.PostgreSQL\Tests.Console.Data.PostgreSQL.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.PostgreSQL", "..\..\Nano.Library\Nano.Data.PostgreSQL\Nano.Data.PostgreSQL.csproj", "{B898497D-7C77-3353-B323-399EBAF53DAC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B898497D-7C77-3353-B323-399EBAF53DAC}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {B898497D-7C77-3353-B323-399EBAF53DAC} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Console.Data.PostgreSQL.csproj b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Console.Data.PostgreSQL.csproj new file mode 100644 index 00000000..a2e4d164 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Console.Data.PostgreSQL.csproj @@ -0,0 +1,49 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..059abbc5 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Console.Data.PostgreSQL.Data.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Console.Data.PostgreSQL.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Models/Example.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Models/Example.cs new file mode 100644 index 00000000..7f869bb9 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Console.Data.PostgreSQL.Data.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContext.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContext.cs new file mode 100644 index 00000000..0266c587 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Console.Data.PostgreSQL.Data; + +/// +public class PostgreSqlDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs new file mode 100644 index 00000000..18a049ec --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Data/PostgreSqlDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.PostgreSQL; + +namespace Console.Data.PostgreSQL.Data; + +/// +public class PostgreSqlDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Dockerfile.Local b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Dockerfile.Local new file mode 100644 index 00000000..5ebef1e3 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260425181509_Initial.Designer.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260425181509_Initial.Designer.cs new file mode 100644 index 00000000..cf3450e8 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260425181509_Initial.Designer.cs @@ -0,0 +1,650 @@ +// +using System; +using Console.Data.PostgreSQL.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Console.Data.PostgreSQL.Migrations +{ + [DbContext(typeof(PostgreSqlDbContext))] + [Migration("20260425181509_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Console.Data.PostgreSQL.Data.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityKey") + .HasColumnType("uuid"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("text"); + + b.Property("OldValue") + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("text"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260425181509_Initial.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260425181509_Initial.cs new file mode 100644 index 00000000..5d011b02 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/20260425181509_Initial.cs @@ -0,0 +1,544 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Console.Data.PostgreSQL.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterDatabase() + .Annotation("Npgsql:PostgresExtension:postgis", ",,"); + + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedBy = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + EntityKey = table.Column(type: "uuid", nullable: false), + EntitySetName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EntityTypeName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + EntityState = table.Column(type: "integer", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + FriendlyName = table.Column(type: "text", nullable: true), + Xml = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IsActive = table.Column(type: "boolean", nullable: false, defaultValue: true), + UserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "boolean", nullable: false), + PasswordHash = table.Column(type: "text", nullable: true), + SecurityStamp = table.Column(type: "text", nullable: true), + ConcurrencyStamp = table.Column(type: "text", nullable: true), + PhoneNumber = table.Column(type: "text", nullable: true), + PhoneNumberConfirmed = table.Column(type: "boolean", nullable: false), + TwoFactorEnabled = table.Column(type: "boolean", nullable: false), + LockoutEnd = table.Column(type: "timestamp with time zone", nullable: true), + LockoutEnabled = table.Column(type: "boolean", nullable: false), + AccessFailedCount = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ParentId = table.Column(type: "uuid", nullable: false), + PropertyName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + RelationName = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NewValue = table.Column(type: "text", nullable: true), + OldValue = table.Column(type: "text", nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + RoleId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Hash = table.Column(type: "text", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + RevokedAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + NewEmail = table.Column(type: "character varying(256)", maxLength: 256, nullable: true), + NewPhoneNumber = table.Column(type: "character varying(20)", maxLength: 20, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + UserId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: true), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "text", nullable: false), + ProviderKey = table.Column(type: "text", nullable: false), + ProviderDisplayName = table.Column(type: "text", nullable: true), + UserId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + IdentityUserId = table.Column(type: "uuid", nullable: false), + AppId = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Value = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "uuid", nullable: false), + LoginProvider = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Value = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ApiKeyId = table.Column(type: "uuid", nullable: false), + ClaimType = table.Column(type: "text", nullable: false), + ClaimValue = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ApiKeyId = table.Column(type: "uuid", nullable: false), + RoleId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs new file mode 100644 index 00000000..623515cd --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Migrations/PostgreSqlDbContextModelSnapshot.cs @@ -0,0 +1,647 @@ +// +using System; +using Console.Data.PostgreSQL.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Console.Data.PostgreSQL.Migrations +{ + [DbContext(typeof(PostgreSqlDbContext))] + partial class PostgreSqlDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Console.Data.PostgreSQL.Data.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("text"); + + b.Property("Xml") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityKey") + .HasColumnType("uuid"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("text"); + + b.Property("OldValue") + .HasColumnType("text"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("text"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("RevokedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApiKeyId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("character varying(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExpireAt") + .HasColumnType("timestamp with time zone"); + + b.Property("IdentityUserId") + .HasColumnType("uuid"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs new file mode 100644 index 00000000..23a3340f --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Program.cs @@ -0,0 +1,13 @@ +using Console.Data.PostgreSQL.Data; +using Nano.App.Console; +using Nano.Data.Extensions; +using Nano.Data.PostgreSQL; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Properties/InternalsVisibleTo.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..efb5ad62 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Data.PostgreSQL")] \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Workers/ExampleWorker.cs b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Workers/ExampleWorker.cs new file mode 100644 index 00000000..fee31fe2 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/Workers/ExampleWorker.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Console.Data.PostgreSQL.Data.Models; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using Nano.Data.Abstractions; + +namespace Console.Data.PostgreSQL.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) +{ + private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); + + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + var example = await this.repository + .AddAsync(new Example + { + Name = "name" + }, cancellationToken); + + await this.repository + .SaveChangesAsync(cancellationToken); + + example = await this.repository + .GetAsync(example.Id, cancellationToken); + + System.Console.WriteLine($"The example name is: '{example!.Name}'"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Development.json b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Development.json new file mode 100644 index 00000000..ddba6f26 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" + } +} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Production.json b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Staging.json b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.json b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.json new file mode 100644 index 00000000..87c218f8 --- /dev/null +++ b/Console.Data.PostgreSQL/Console.Data.PostgreSQL/appsettings.json @@ -0,0 +1,24 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null + } +} \ No newline at end of file diff --git a/Console.Data.PostgreSQL/Dockerfile b/Console.Data.PostgreSQL/Dockerfile new file mode 100644 index 00000000..b9fe9470 --- /dev/null +++ b/Console.Data.PostgreSQL/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Data.PostgreSQL.dll"] \ No newline at end of file diff --git a/Console.Data.PostgreSQL/README.md b/Console.Data.PostgreSQL/README.md new file mode 100644 index 00000000..d27443e3 --- /dev/null +++ b/Console.Data.PostgreSQL/README.md @@ -0,0 +1,186 @@ +# Console.Data.PostgreSQL + +> _Nano API application with postgresql data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings)**, +and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context)**. + +The worker creates and retreives a `Example` entity using the Nano **[Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories)**. Run the +application and observe the console output generated by the `ExampleWorker`. + +> 📖 Learn more about **[Nano.Data.PostgreSQL](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.PostgreSQL/README.md#nanodatamysql)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +Also, an initial migration has been added to the project. + +```powershell +dotnet ef migrations add Initial --project Console.Data.PostgreSQL +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null +} +``` + +...and `appsettings.Development.json` + +```json +"Data": { + "StartupAction": "Migrate", + "ConnectionString": "Host=host.docker.internal;Port=5432;Database=nanoDb;Username=sa;Password=myPassword_123" +} +``` + +## Docker Compose +Added PostgreSQL as a service dependency in `docker-compose.yml`. + +```yaml +services: + console.data.postgresql: + depends_on: + - database + + database: + image: postgis/postgis:latest + ports: + - 5432:5432 + networks: + - network + environment: + POSTGRES_USER: sa + POSTGRES_PASSWORD: myPassword_123 + POSTGRES_DB: nanoDb +``` + +## Kubernetes +Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. + +```json +spec: + template: + spec: + containers: + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SQL_NAME: nanoDb + SQL_USER: api-data-postgres-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +``` + +Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. + +```yaml +- name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "5432"; + $env:SQL_ADMIN_USER = az postgres flexible-server list -g $env:AZURE_GROUP_DATABASE --query "[0].username" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Host=$env:SQL_HOST;Port=$env:SQL_PORT;Database=$env:SQL_NAME;Username=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;SSL Mode=Prefer;Trust Server Certificate=true"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y postgresql-client + + $userExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "CREATE ROLE $env:SQL_USER WITH LOGIN PASSWORD '$env:SQL_PASSWORD';" + } + + $userDbExists = psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -tAc "SELECT 1 FROM pg_roles WHERE rolname='$env:SQL_USER';" + + if ($userDbExists -ne "1") + { + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT CONNECT ON DATABASE $env:SQL_NAME TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT USAGE ON SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO $env:SQL_USER;" + + psql "$env:SQL_MIGRATION_CONNECTIONSTRING" ` + -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO $env:SQL_USER;" + } +``` + +Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. diff --git a/Console.Data.SqLite/.docker/docker-compose.dcproj b/Console.Data.SqLite/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Data.SqLite/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Data.SqLite/.docker/docker-compose.yml b/Console.Data.SqLite/.docker/docker-compose.yml new file mode 100644 index 00000000..2d2a2508 --- /dev/null +++ b/Console.Data.SqLite/.docker/docker-compose.yml @@ -0,0 +1,16 @@ +services: + console.data.sqlite: + image: console.data.sqlite + restart: on-failure + build: + context: ../Console.Data.SqLite + dockerfile: "Dockerfile.Local" + volumes: + - ./bin/data:/mnt/data + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.Data.SqLite/.dockerignore b/Console.Data.SqLite/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Data.SqLite/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Data.SqLite/.github/config/slack.yml b/Console.Data.SqLite/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Data.SqLite/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.SqLite/.github/workflows/build-and-deploy.yml b/Console.Data.SqLite/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..86a7f66c --- /dev/null +++ b/Console.Data.SqLite/.github/workflows/build-and-deploy.yml @@ -0,0 +1,181 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Data.SqLite + IMAGE_NAME: console.data.sqlite + SERVICE_NAME: console-data-sqlite + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/Nano-Core/Nano.Template.Console + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + SQL_NAME: nanoDb + SQL_SIZE: 10Gi + SQL_CONNECTIONSTRING: "Data Source=/mnt/data/{{ env.SQL_NAME }}.sqlite" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Database Migration + shell: pwsh + run: | + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/data-storageclass.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/data-storageclass.tmp.yaml; + kubectl apply -f .kubernetes/data-storageclass.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/data-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/data-pvc.tmp.yaml; + kubectl apply -f .kubernetes/data-pvc.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.SqLite/.gitignore b/Console.Data.SqLite/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Data.SqLite/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.SqLite/.kubernetes/configmap.yaml b/Console.Data.SqLite/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Data.SqLite/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.SqLite/.kubernetes/cronjob.yaml b/Console.Data.SqLite/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..aa4f8991 --- /dev/null +++ b/Console.Data.SqLite/.kubernetes/cronjob.yaml @@ -0,0 +1,58 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/data + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret diff --git a/Console.Data.SqLite/.kubernetes/data-pvc.yaml b/Console.Data.SqLite/.kubernetes/data-pvc.yaml new file mode 100644 index 00000000..0b9d4415 --- /dev/null +++ b/Console.Data.SqLite/.kubernetes/data-pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %SERVICE_NAME%-data-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: %SERVICE_NAME%-data-storage-class + resources: + requests: + storage: %DATA_SIZE% \ No newline at end of file diff --git a/Console.Data.SqLite/.kubernetes/data-storageclass.yaml b/Console.Data.SqLite/.kubernetes/data-storageclass.yaml new file mode 100644 index 00000000..47530cdc --- /dev/null +++ b/Console.Data.SqLite/.kubernetes/data-storageclass.yaml @@ -0,0 +1,10 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: %SERVICE_NAME%-data-storage-class +provisioner: kubernetes.io/azure-disk +parameters: + storageaccounttype: Standard_LRS + kind: Managed +reclaimPolicy: Retain +volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Properties/DoNotParallelize.cs b/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Tests.Console.Data.SqLite.csproj b/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Tests.Console.Data.SqLite.csproj new file mode 100644 index 00000000..b5e793e0 --- /dev/null +++ b/Console.Data.SqLite/.tests/Tests.Console.Data.SqLite/Tests.Console.Data.SqLite.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Data.SqLite/Console.Data.SqLite.sln b/Console.Data.SqLite/Console.Data.SqLite.sln new file mode 100644 index 00000000..365797b7 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite.sln @@ -0,0 +1,139 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.SqLite", "Console.Data.SqLite\Console.Data.SqLite.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + .kubernetes\data-pvc.yaml = .kubernetes\data-pvc.yaml + .kubernetes\data-storageclass.yaml = .kubernetes\data-storageclass.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.SqLite", ".tests\Tests.Console.Data.SqLite\Tests.Console.Data.SqLite.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqLite", "..\..\Nano.Library\Nano.Data.SqLite\Nano.Data.SqLite.csproj", "{0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0D1F1D5D-B1DF-443F-2630-11DC6DEEDF00} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Data.SqLite/Console.Data.SqLite/Console.Data.SqLite.csproj b/Console.Data.SqLite/Console.Data.SqLite/Console.Data.SqLite.csproj new file mode 100644 index 00000000..43996cd4 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Console.Data.SqLite.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Data/Mappings/ExampleMapping.cs b/Console.Data.SqLite/Console.Data.SqLite/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..bbbf1fab --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Console.Data.SqLite.Data.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Console.Data.SqLite.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Data/Models/Example.cs b/Console.Data.SqLite/Console.Data.SqLite/Data/Models/Example.cs new file mode 100644 index 00000000..ba4c0efc --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Data/Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Console.Data.SqLite.Data.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContext.cs b/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContext.cs new file mode 100644 index 00000000..843fde90 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Console.Data.SqLite.Data; + +/// +public class SqLiteDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContextFactory.cs b/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContextFactory.cs new file mode 100644 index 00000000..f202fe8a --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Data/SqLiteDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.SqLite; + +namespace Console.Data.SqLite.Data; + +/// +public class SqLiteDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Dockerfile.Local b/Console.Data.SqLite/Console.Data.SqLite/Dockerfile.Local new file mode 100644 index 00000000..98887f04 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Data.SqLite.dll"] \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260427110613_Initial.Designer.cs b/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260427110613_Initial.Designer.cs new file mode 100644 index 00000000..f33a1ac1 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260427110613_Initial.Designer.cs @@ -0,0 +1,638 @@ +// +using System; +using Console.Data.SqLite.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Console.Data.SqLite.Migrations +{ + [DbContext(typeof(SqLiteDbContext))] + [Migration("20260427110613_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.7"); + + modelBuilder.Entity("Console.Data.SqLite.Data.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FriendlyName") + .HasColumnType("TEXT"); + + b.Property("Xml") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EntityKey") + .HasColumnType("TEXT"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("NewValue") + .HasColumnType("TEXT"); + + b.Property("OldValue") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RevokedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260427110613_Initial.cs b/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260427110613_Initial.cs new file mode 100644 index 00000000..49419129 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Migrations/20260427110613_Initial.cs @@ -0,0 +1,540 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Console.Data.SqLite.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + CreatedBy = table.Column(type: "TEXT", maxLength: 256, nullable: false), + EntityKey = table.Column(type: "TEXT", nullable: false), + EntitySetName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EntityTypeName = table.Column(type: "TEXT", maxLength: 256, nullable: false), + EntityState = table.Column(type: "INTEGER", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "TEXT", maxLength: 256, nullable: true), + IsDeleted = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + FriendlyName = table.Column(type: "TEXT", nullable: true), + Xml = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + IsActive = table.Column(type: "INTEGER", nullable: false, defaultValue: true), + UserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + Email = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "INTEGER", nullable: false), + PasswordHash = table.Column(type: "TEXT", nullable: true), + SecurityStamp = table.Column(type: "TEXT", nullable: true), + ConcurrencyStamp = table.Column(type: "TEXT", nullable: true), + PhoneNumber = table.Column(type: "TEXT", nullable: true), + PhoneNumberConfirmed = table.Column(type: "INTEGER", nullable: false), + TwoFactorEnabled = table.Column(type: "INTEGER", nullable: false), + LockoutEnd = table.Column(type: "TEXT", nullable: true), + LockoutEnabled = table.Column(type: "INTEGER", nullable: false), + AccessFailedCount = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + IsDeleted = table.Column(type: "INTEGER", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ParentId = table.Column(type: "TEXT", nullable: false), + PropertyName = table.Column(type: "TEXT", maxLength: 256, nullable: false), + RelationName = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NewValue = table.Column(type: "TEXT", nullable: true), + OldValue = table.Column(type: "TEXT", nullable: true), + IsDeleted = table.Column(type: "INTEGER", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + RoleId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + IdentityUserId = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", maxLength: 256, nullable: false), + Hash = table.Column(type: "TEXT", nullable: false), + CreatedAt = table.Column(type: "TEXT", nullable: false), + RevokedAt = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + IdentityUserId = table.Column(type: "TEXT", nullable: false), + NewEmail = table.Column(type: "TEXT", maxLength: 256, nullable: true), + NewPhoneNumber = table.Column(type: "TEXT", maxLength: 20, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: true), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "TEXT", nullable: false), + ProviderKey = table.Column(type: "TEXT", nullable: false), + ProviderDisplayName = table.Column(type: "TEXT", nullable: true), + UserId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + IdentityUserId = table.Column(type: "TEXT", nullable: false), + AppId = table.Column(type: "TEXT", maxLength: 256, nullable: false), + Value = table.Column(type: "TEXT", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "TEXT", nullable: false), + LoginProvider = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Value = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ApiKeyId = table.Column(type: "TEXT", nullable: false), + ClaimType = table.Column(type: "TEXT", nullable: false), + ClaimValue = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + ApiKeyId = table.Column(type: "TEXT", nullable: false), + RoleId = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Console.Data.SqLite/Console.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs b/Console.Data.SqLite/Console.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs new file mode 100644 index 00000000..ed2c03c5 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Migrations/SqLiteDbContextModelSnapshot.cs @@ -0,0 +1,635 @@ +// +using System; +using Console.Data.SqLite.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Console.Data.SqLite.Migrations +{ + [DbContext(typeof(SqLiteDbContext))] + partial class SqLiteDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.7"); + + modelBuilder.Entity("Console.Data.SqLite.Data.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FriendlyName") + .HasColumnType("TEXT"); + + b.Property("Xml") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EntityKey") + .HasColumnType("TEXT"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsDeleted") + .HasColumnType("INTEGER"); + + b.Property("NewValue") + .HasColumnType("TEXT"); + + b.Property("OldValue") + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("TEXT"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("RevokedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApiKeyId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique(); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("PhoneNumber") + .IsUnique(); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("ExpireAt") + .HasColumnType("TEXT"); + + b.Property("IdentityUserId") + .HasColumnType("TEXT"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Console.Data.SqLite/Console.Data.SqLite/Program.cs b/Console.Data.SqLite/Console.Data.SqLite/Program.cs new file mode 100644 index 00000000..7cdf911d --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Program.cs @@ -0,0 +1,15 @@ +using Console.Data.SqLite.Data; +using Nano.App.Console; +using Nano.Data.Extensions; +using Nano.Data.SqLite; +using System; +using System.IO; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Properties/InternalsVisibleTo.cs b/Console.Data.SqLite/Console.Data.SqLite/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..3b8565f7 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Data.SqLite")] \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/Workers/ExampleWorker.cs b/Console.Data.SqLite/Console.Data.SqLite/Workers/ExampleWorker.cs new file mode 100644 index 00000000..eb84169a --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/Workers/ExampleWorker.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Console.Data.SqLite.Data.Models; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using Nano.Data.Abstractions; + +namespace Console.Data.SqLite.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) +{ + private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); + + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + var example = await this.repository + .AddAsync(new Example + { + Name = "name" + }, cancellationToken); + + await this.repository + .SaveChangesAsync(cancellationToken); + + example = await this.repository + .GetAsync(example.Id, cancellationToken); + + System.Console.WriteLine($"The example name is: '{example!.Name}'"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json new file mode 100644 index 00000000..8927ff99 --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Development.json @@ -0,0 +1,5 @@ +{ + "Data": { + "StartupAction": "Migrate" + } +} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Production.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.Staging.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.SqLite/Console.Data.SqLite/appsettings.json b/Console.Data.SqLite/Console.Data.SqLite/appsettings.json new file mode 100644 index 00000000..8e22deeb --- /dev/null +++ b/Console.Data.SqLite/Console.Data.SqLite/appsettings.json @@ -0,0 +1,24 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "Migrate", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": "Data Source=/mnt/data/nanoDb.sqlite", + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null + } +} \ No newline at end of file diff --git a/Console.Data.SqLite/Dockerfile b/Console.Data.SqLite/Dockerfile new file mode 100644 index 00000000..bc4e676b --- /dev/null +++ b/Console.Data.SqLite/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Data.SqLite.dll"] \ No newline at end of file diff --git a/Console.Data.SqLite/README.md b/Console.Data.SqLite/README.md new file mode 100644 index 00000000..7661aa11 --- /dev/null +++ b/Console.Data.SqLite/README.md @@ -0,0 +1,151 @@ +# Console.Data.SqLite + +> _Nano API application with sqlite data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings)**, +and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context)**. + +The worker creates and retreives a `Example` entity using the Nano **[Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories)**. Run the +application and observe the console output generated by the `ExampleWorker`. + +> 📖 Learn more about **[Nano.Data.SqLite](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqLite/README.md#nanodatamysql)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +Also, an initial migration has been added to the project. + +```powershell +dotnet ef migrations add Initial --project Console.Data.SqLite +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": "Data Source=/data/nanoDb.sqlite", + "Repository": { + "UseAutoSave": false, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null +} +``` + +...and `appsettings.Development.json` + +```json +"Data": { + "StartupAction": "Migrate", +} +``` + +## Docker Compose +Added SqLite as a service dependency in `docker-compose.yml`. + +```yaml +services: + console.data.sqlite: + volumes: + - ./bin/data:/mnt/data +``` + +## Kubernetes +Added two additional kubernetes templates, `storageclass.yaml` and `pvc.yaml`, for dynamically manage and creating the disk for the SqLite database. + +Also, updated `cronjob.yaml` adding the volumes and volume mounts. + +```json +spec: + jobTemplate: + spec: + template: + spec: + containers: + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/data + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SQL_NAME: nanoDb + SQL_SIZE: 10Gi + SQL_CONNECTIONSTRING: "Data Source=/mnt/data/{{ env.nanoDb }}.sqlite" +``` + +Additionally, this step has been added to ensure database migrations are applied. + +```yaml +- name: Database Migration + shell: pwsh + run: | + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING" `; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; +``` + +Deployment commands have also been updated to apply each of the new Kubernetes templates. + +```powershell +Get-Content .kubernetes/{resource-name}.yaml ` + | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` + | Set-Content .kubernetes/{resource-name}.tmp.yaml; + +sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; +``` diff --git a/Console.Data.SqlServer/.docker/docker-compose.dcproj b/Console.Data.SqlServer/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Data.SqlServer/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Data.SqlServer/.docker/docker-compose.yml b/Console.Data.SqlServer/.docker/docker-compose.yml new file mode 100644 index 00000000..423dc083 --- /dev/null +++ b/Console.Data.SqlServer/.docker/docker-compose.yml @@ -0,0 +1,27 @@ +services: + console.data.sqlserver: + image: console.data.sqlserver + restart: on-failure + build: + context: ../Console.Data.SqlServer + dockerfile: "Dockerfile.Local" + depends_on: + - database + networks: + - network + + database: + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - 1433:1433 + networks: + - network + environment: + SA_PASSWORD: myPassword_123 + ACCEPT_EULA: Y + MSSQL_PID: Developer + +networks: + network: + name: network + external: true diff --git a/Console.Data.SqlServer/.dockerignore b/Console.Data.SqlServer/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Data.SqlServer/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Data.SqlServer/.github/config/slack.yml b/Console.Data.SqlServer/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Data.SqlServer/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml b/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..054564d4 --- /dev/null +++ b/Console.Data.SqlServer/.github/workflows/build-and-deploy.yml @@ -0,0 +1,227 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Data.SqlServer + IMAGE_NAME: console.data.sqlserver + SERVICE_NAME: console-data-sqlserver + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + SQL_NAME: nanoDb + SQL_USER: api-data-sqlserver-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + + - name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "1433" + $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y mssql-tools unixodbc-dev + + $loginExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" + + if ($loginExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" + }; + + $userExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" + + if ($userExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; + ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; + ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" + }; + + echo "SQL_HOST=$env:SQL_HOST" >> $env:GITHUB_ENV; + echo "SQL_PORT=$env:SQL_PORT" >> $env:GITHUB_ENV; + + - name: Kubernetes Deploy + shell: pwsh + run: | + $env:SQL_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_USER;Password=$env:SQL_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + Get-Content .kubernetes/auth-sql-secret.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/auth-sql-secret.tmp.yaml; + kubectl apply -f .kubernetes/auth-sql-secret.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Data.SqlServer/.gitignore b/Console.Data.SqlServer/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Data.SqlServer/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Data.SqlServer/.kubernetes/auth-sql-secret.yaml b/Console.Data.SqlServer/.kubernetes/auth-sql-secret.yaml new file mode 100644 index 00000000..fffa83c4 --- /dev/null +++ b/Console.Data.SqlServer/.kubernetes/auth-sql-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: $env:SERVICE_NAME-sql-auth-secret + namespace: %KUBERNETES_NAMESPACE% +type: Opaque +stringData: + data-connectionstring: %SQL_CONNECTIONSTRING% + diff --git a/Console.Data.SqlServer/.kubernetes/configmap.yaml b/Console.Data.SqlServer/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Data.SqlServer/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Data.SqlServer/.kubernetes/cronjob.yaml b/Console.Data.SqlServer/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..9ae388c3 --- /dev/null +++ b/Console.Data.SqlServer/.kubernetes/cronjob.yaml @@ -0,0 +1,57 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret diff --git a/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Properties/DoNotParallelize.cs b/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Tests.Console.Data.SqlServer.csproj b/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Tests.Console.Data.SqlServer.csproj new file mode 100644 index 00000000..c97cb86e --- /dev/null +++ b/Console.Data.SqlServer/.tests/Tests.Console.Data.SqlServer/Tests.Console.Data.SqlServer.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Data.SqlServer/Console.Data.SqlServer.sln b/Console.Data.SqlServer/Console.Data.SqlServer.sln new file mode 100644 index 00000000..32adc5d0 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer.sln @@ -0,0 +1,138 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Data.SqlServer", "Console.Data.SqlServer\Console.Data.SqlServer.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\auth-sql-secret.yaml = .kubernetes\auth-sql-secret.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Data.SqlServer", ".tests\Tests.Console.Data.SqlServer\Tests.Console.Data.SqlServer.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.SqlServer", "..\..\Nano.Library\Nano.Data.SqlServer\Nano.Data.SqlServer.csproj", "{FE8B04D5-8AED-F023-588B-65947B83FDF5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data", "..\..\Nano.Library\Nano.Data\Nano.Data.csproj", "{A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE8B04D5-8AED-F023-588B-65947B83FDF5}.Release|Any CPU.Build.0 = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {FE8B04D5-8AED-F023-588B-65947B83FDF5} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {A1CC2D8C-C323-51C2-2480-BFFD9DAD9F1F} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Console.Data.SqlServer.csproj b/Console.Data.SqlServer/Console.Data.SqlServer/Console.Data.SqlServer.csproj new file mode 100644 index 00000000..90a4a93b --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Console.Data.SqlServer.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Data/Mappings/ExampleMapping.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Data/Mappings/ExampleMapping.cs new file mode 100644 index 00000000..dba812d8 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Data/Mappings/ExampleMapping.cs @@ -0,0 +1,26 @@ +using System; +using Console.Data.SqlServer.Data.Models; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Nano.Data.Mappings; + +namespace Console.Data.SqlServer.Data.Mappings; + +/// +/// Example Mapping. +/// +public class ExampleMapping : BaseEntityMapping +{ + /// + public override void Configure(EntityTypeBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + + base.Configure(builder); + + builder + .Property(x => x.Name); + + builder + .HasIndex(x => x.Name); + } +} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Data/Models/Example.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Data/Models/Example.cs new file mode 100644 index 00000000..d60dc3f2 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Data/Models/Example.cs @@ -0,0 +1,14 @@ +using Nano.Data.Abstractions.Models; + +namespace Console.Data.SqlServer.Data.Models; + +/// +/// Example. +/// +public class Example : BaseEntity +{ + /// + /// Name. + /// + public virtual string Name { get; set; } = null!; +} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContext.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContext.cs new file mode 100644 index 00000000..81776d33 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContext.cs @@ -0,0 +1,10 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Nano.Data; +using Nano.Data.Abstractions.Config; + +namespace Console.Data.SqlServer.Data; + +/// +public class SqlServerDbContext(DbContextOptions contextOptions, IOptionsMonitor dataOptions) + : BaseDbContext(contextOptions, dataOptions); \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContextFactory.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContextFactory.cs new file mode 100644 index 00000000..83b89567 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Data/SqlServerDbContextFactory.cs @@ -0,0 +1,7 @@ +using Nano.Data; +using Nano.Data.SqlServer; + +namespace Console.Data.SqlServer.Data; + +/// +public class SqlServerDbContextFactory : BaseDbContextFactory; \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Dockerfile.Local b/Console.Data.SqlServer/Console.Data.SqlServer/Dockerfile.Local new file mode 100644 index 00000000..14a7956b --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260425181618_Initial.Designer.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260425181618_Initial.Designer.cs new file mode 100644 index 00000000..1d670d2a --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260425181618_Initial.Designer.cs @@ -0,0 +1,653 @@ +// +using System; +using Console.Data.SqlServer.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Console.Data.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + [Migration("20260425181618_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Console.Data.SqlServer.Data.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("nvarchar(max)"); + + b.Property("Xml") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityKey") + .HasColumnType("uniqueidentifier"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("nvarchar(max)"); + + b.Property("OldValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(450)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetimeoffset"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260425181618_Initial.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260425181618_Initial.cs new file mode 100644 index 00000000..63eed93a --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/20260425181618_Initial.cs @@ -0,0 +1,544 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Console.Data.SqlServer.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "__EFAudit", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + CreatedBy = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EntityKey = table.Column(type: "uniqueidentifier", nullable: false), + EntitySetName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EntityTypeName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + EntityState = table.Column(type: "int", nullable: false, defaultValue: 0), + RequestId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAudit", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFDataProtectionKeys", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + FriendlyName = table.Column(type: "nvarchar(max)", nullable: true), + Xml = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFDataProtectionKeys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRole", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRole", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUser", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IsActive = table.Column(type: "bit", nullable: false, defaultValue: true), + UserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedUserName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + Email = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NormalizedEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + EmailConfirmed = table.Column(type: "bit", nullable: false), + PasswordHash = table.Column(type: "nvarchar(max)", nullable: true), + SecurityStamp = table.Column(type: "nvarchar(max)", nullable: true), + ConcurrencyStamp = table.Column(type: "nvarchar(max)", nullable: true), + PhoneNumber = table.Column(type: "nvarchar(450)", nullable: true), + PhoneNumberConfirmed = table.Column(type: "bit", nullable: false), + TwoFactorEnabled = table.Column(type: "bit", nullable: false), + LockoutEnd = table.Column(type: "datetimeoffset", nullable: true), + LockoutEnabled = table.Column(type: "bit", nullable: false), + AccessFailedCount = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUser", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Example", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + IsDeleted = table.Column(type: "bigint", nullable: false, defaultValue: 0L), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Example", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "__EFAuditProperties", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ParentId = table.Column(type: "uniqueidentifier", nullable: false), + PropertyName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + RelationName = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NewValue = table.Column(type: "nvarchar(max)", nullable: true), + OldValue = table.Column(type: "nvarchar(max)", nullable: true), + IsDeleted = table.Column(type: "bigint", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFAuditProperties", x => x.Id); + table.ForeignKey( + name: "FK___EFAuditProperties___EFAudit_ParentId", + column: x => x.ParentId, + principalTable: "__EFAudit", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityRoleClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + RoleId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityRoleClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityRoleClaim___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKey", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + Name = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Hash = table.Column(type: "nvarchar(max)", nullable: false), + CreatedAt = table.Column(type: "datetimeoffset", nullable: false), + RevokedAt = table.Column(type: "datetimeoffset", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKey", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKey___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserChangeData", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + NewEmail = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: true), + NewPhoneNumber = table.Column(type: "nvarchar(20)", maxLength: 20, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserChangeData", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserChangeData___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserClaim", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + UserId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(max)", nullable: true), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserClaim___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserLogin", + columns: table => new + { + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + ProviderKey = table.Column(type: "nvarchar(450)", nullable: false), + ProviderDisplayName = table.Column(type: "nvarchar(max)", nullable: true), + UserId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserLogin", x => new { x.LoginProvider, x.ProviderKey }); + table.ForeignKey( + name: "FK___EFIdentityUserLogin___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRefreshToken", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + IdentityUserId = table.Column(type: "uniqueidentifier", nullable: false), + AppId = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + Value = table.Column(type: "nvarchar(256)", maxLength: 256, nullable: false), + ExpireAt = table.Column(type: "datetimeoffset", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRefreshToken", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityUserRefreshToken___EFIdentityUser_IdentityUserId", + column: x => x.IdentityUserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserRole", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserRole", x => new { x.UserId, x.RoleId }); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityUserRole___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityUserToken", + columns: table => new + { + UserId = table.Column(type: "uniqueidentifier", nullable: false), + LoginProvider = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Value = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityUserToken", x => new { x.UserId, x.LoginProvider, x.Name }); + table.ForeignKey( + name: "FK___EFIdentityUserToken___EFIdentityUser_UserId", + column: x => x.UserId, + principalTable: "__EFIdentityUser", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyClaim", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), + ClaimType = table.Column(type: "nvarchar(450)", nullable: false), + ClaimValue = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyClaim", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyClaim___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "__EFIdentityApiKeyRole", + columns: table => new + { + Id = table.Column(type: "uniqueidentifier", nullable: false), + ApiKeyId = table.Column(type: "uniqueidentifier", nullable: false), + RoleId = table.Column(type: "uniqueidentifier", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK___EFIdentityApiKeyRole", x => x.Id); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityApiKey_ApiKeyId", + column: x => x.ApiKeyId, + principalTable: "__EFIdentityApiKey", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK___EFIdentityApiKeyRole___EFIdentityRole_RoleId", + column: x => x.RoleId, + principalTable: "__EFIdentityRole", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_CreatedBy", + table: "__EFAudit", + column: "CreatedBy"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityKey", + table: "__EFAudit", + column: "EntityKey"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityState", + table: "__EFAudit", + column: "EntityState"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_EntityTypeName", + table: "__EFAudit", + column: "EntityTypeName"); + + migrationBuilder.CreateIndex( + name: "IX___EFAudit_RequestId", + table: "__EFAudit", + column: "RequestId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_ParentId", + table: "__EFAuditProperties", + column: "ParentId"); + + migrationBuilder.CreateIndex( + name: "IX___EFAuditProperties_PropertyName", + table: "__EFAuditProperties", + column: "PropertyName"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_IdentityUserId", + table: "__EFIdentityApiKey", + column: "IdentityUserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKey_RevokedAt", + table: "__EFIdentityApiKey", + column: "RevokedAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType", + table: "__EFIdentityApiKeyClaim", + columns: new[] { "ApiKeyId", "ClaimType" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityApiKeyRole_RoleId", + table: "__EFIdentityApiKeyRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityApiKeyRole_ApiKeyId_RoleId", + table: "__EFIdentityApiKeyRole", + columns: new[] { "ApiKeyId", "RoleId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "RoleNameIndex", + table: "__EFIdentityRole", + column: "NormalizedName", + unique: true, + filter: "[NormalizedName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityRoleClaim_RoleId", + table: "__EFIdentityRoleClaim", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "EmailIndex", + table: "__EFIdentityUser", + column: "NormalizedEmail"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_Email", + table: "__EFIdentityUser", + column: "Email", + unique: true, + filter: "[Email] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_IsActive", + table: "__EFIdentityUser", + column: "IsActive"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUser_PhoneNumber", + table: "__EFIdentityUser", + column: "PhoneNumber", + unique: true, + filter: "[PhoneNumber] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "UserNameIndex", + table: "__EFIdentityUser", + column: "NormalizedUserName", + unique: true, + filter: "[NormalizedUserName] IS NOT NULL"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserChangeData_IdentityUserId", + table: "__EFIdentityUserChangeData", + column: "IdentityUserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserClaim_UserId", + table: "__EFIdentityUserClaim", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserLogin_UserId", + table: "__EFIdentityUserLogin", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRefreshToken_ExpireAt", + table: "__EFIdentityUserRefreshToken", + column: "ExpireAt"); + + migrationBuilder.CreateIndex( + name: "UX___EFIdentityUserRefreshToken_IdentityUserId_AppId", + table: "__EFIdentityUserRefreshToken", + columns: new[] { "IdentityUserId", "AppId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX___EFIdentityUserRole_RoleId", + table: "__EFIdentityUserRole", + column: "RoleId"); + + migrationBuilder.CreateIndex( + name: "IX_Example_CreatedAt", + table: "Example", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Example_IsDeleted", + table: "Example", + column: "IsDeleted"); + + migrationBuilder.CreateIndex( + name: "IX_Example_Name", + table: "Example", + column: "Name"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "__EFAuditProperties"); + + migrationBuilder.DropTable( + name: "__EFDataProtectionKeys"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKeyRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityRoleClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserChangeData"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserClaim"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserLogin"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRefreshToken"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUserToken"); + + migrationBuilder.DropTable( + name: "Example"); + + migrationBuilder.DropTable( + name: "__EFAudit"); + + migrationBuilder.DropTable( + name: "__EFIdentityApiKey"); + + migrationBuilder.DropTable( + name: "__EFIdentityRole"); + + migrationBuilder.DropTable( + name: "__EFIdentityUser"); + } + } +} diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs new file mode 100644 index 00000000..6b10f6b3 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Migrations/SqlServerDbContextModelSnapshot.cs @@ -0,0 +1,650 @@ +// +using System; +using Console.Data.SqlServer.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Console.Data.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerDbContext))] + partial class SqlServerDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Console.Data.SqlServer.Data.Models.Example", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasDefaultValue(0L); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("IsDeleted"); + + b.HasIndex("Name"); + + b.ToTable("Example"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.DataProtection.EntityFrameworkCore.DataProtectionKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("FriendlyName") + .HasColumnType("nvarchar(max)"); + + b.Property("Xml") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("__EFDataProtectionKeys", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex") + .HasFilter("[NormalizedName] IS NOT NULL"); + + b.ToTable("__EFIdentityRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityRoleClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("nvarchar(max)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserClaim", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderKey") + .HasColumnType("nvarchar(450)"); + + b.Property("ProviderDisplayName") + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("__EFIdentityUserLogin", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("__EFIdentityUserRole", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uniqueidentifier"); + + b.Property("LoginProvider") + .HasColumnType("nvarchar(450)"); + + b.Property("Name") + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .HasColumnType("nvarchar(max)"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("__EFIdentityUserToken", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("CreatedBy") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityKey") + .HasColumnType("uniqueidentifier"); + + b.Property("EntitySetName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EntityState") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasDefaultValue(0); + + b.Property("EntityTypeName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("RequestId") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("CreatedBy"); + + b.HasIndex("EntityKey"); + + b.HasIndex("EntityState"); + + b.HasIndex("EntityTypeName"); + + b.HasIndex("RequestId"); + + b.ToTable("__EFAudit", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("IsDeleted") + .HasColumnType("bigint"); + + b.Property("NewValue") + .HasColumnType("nvarchar(max)"); + + b.Property("OldValue") + .HasColumnType("nvarchar(max)"); + + b.Property("ParentId") + .HasColumnType("uniqueidentifier"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RelationName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ParentId"); + + b.HasIndex("PropertyName"); + + b.ToTable("__EFAuditProperties", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("CreatedAt") + .HasColumnType("datetimeoffset"); + + b.Property("Hash") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("RevokedAt") + .HasColumnType("datetimeoffset"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId"); + + b.HasIndex("RevokedAt"); + + b.ToTable("__EFIdentityApiKey", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("ClaimType") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("ClaimValue") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.HasIndex("ApiKeyId", "ClaimType") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyClaim_ApiKeyId_ClaimType"); + + b.ToTable("__EFIdentityApiKeyClaim", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("ApiKeyId") + .HasColumnType("uniqueidentifier"); + + b.Property("RoleId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.HasIndex("ApiKeyId", "RoleId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityApiKeyRole_ApiKeyId_RoleId"); + + b.ToTable("__EFIdentityApiKeyRole", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("NewEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NewPhoneNumber") + .HasMaxLength(20) + .HasColumnType("nvarchar(20)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityUserId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserChangeData_IdentityUserId"); + + b.ToTable("__EFIdentityUserChangeData", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("nvarchar(max)"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("bit"); + + b.Property("IsActive") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true); + + b.Property("LockoutEnabled") + .HasColumnType("bit"); + + b.Property("LockoutEnd") + .HasColumnType("datetimeoffset"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("nvarchar(max)"); + + b.Property("PhoneNumber") + .HasColumnType("nvarchar(450)"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("bit"); + + b.Property("SecurityStamp") + .HasColumnType("nvarchar(max)"); + + b.Property("TwoFactorEnabled") + .HasColumnType("bit"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("Email") + .IsUnique() + .HasFilter("[Email] IS NOT NULL"); + + b.HasIndex("IsActive"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex") + .HasFilter("[NormalizedUserName] IS NOT NULL"); + + b.HasIndex("PhoneNumber") + .IsUnique() + .HasFilter("[PhoneNumber] IS NOT NULL"); + + b.ToTable("__EFIdentityUser", (string)null); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uniqueidentifier"); + + b.Property("AppId") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.Property("ExpireAt") + .HasColumnType("datetimeoffset"); + + b.Property("IdentityUserId") + .HasColumnType("uniqueidentifier"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("nvarchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("ExpireAt"); + + b.HasIndex("IdentityUserId", "AppId") + .IsUnique() + .HasDatabaseName("UX___EFIdentityUserRefreshToken_IdentityUserId_AppId"); + + b.ToTable("__EFIdentityUserRefreshToken", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntryProperty", b => + { + b.HasOne("Nano.Data.Abstractions.Models.AuditEntry", "Parent") + .WithMany("Properties") + .HasForeignKey("ParentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyClaim", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityApiKeyRole", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityApiKey", "ApiKey") + .WithMany() + .HasForeignKey("ApiKeyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", "Role") + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ApiKey"); + + b.Navigation("Role"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithOne() + .HasForeignKey("Nano.Data.Abstractions.Models.Identity.IdentityUserChangeData", "IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.Identity.IdentityUserRefreshToken", b => + { + b.HasOne("Nano.Data.Abstractions.Models.Identity.IdentityUserEx", "IdentityUser") + .WithMany() + .HasForeignKey("IdentityUserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("IdentityUser"); + }); + + modelBuilder.Entity("Nano.Data.Abstractions.Models.AuditEntry", b => + { + b.Navigation("Properties"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Program.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Program.cs new file mode 100644 index 00000000..ac3119ae --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Program.cs @@ -0,0 +1,13 @@ +using Console.Data.SqlServer.Data; +using Nano.App.Console; +using Nano.Data.Extensions; +using Nano.Data.SqlServer; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoData(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Properties/InternalsVisibleTo.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..a42febc8 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Data.SqlServer")] \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/Workers/ExampleWorker.cs b/Console.Data.SqlServer/Console.Data.SqlServer/Workers/ExampleWorker.cs new file mode 100644 index 00000000..35606102 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/Workers/ExampleWorker.cs @@ -0,0 +1,45 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Console.Data.SqlServer.Data.Models; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using Nano.Data.Abstractions; + +namespace Console.Data.SqlServer.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IRepository repository) : BaseWorker(logger) +{ + private readonly IRepository repository = repository ?? throw new ArgumentNullException(nameof(repository)); + + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + var example = await this.repository + .AddAsync(new Example + { + Name = "name" + }, cancellationToken); + + await this.repository + .SaveChangesAsync(cancellationToken); + + example = await this.repository + .GetAsync(example.Id, cancellationToken); + + System.Console.WriteLine($"The example name is: '{example!.Name}'"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Development.json b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Development.json new file mode 100644 index 00000000..960866dc --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Development.json @@ -0,0 +1,6 @@ +{ + "Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" + } +} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Production.json b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Staging.json b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.json b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.json new file mode 100644 index 00000000..87c218f8 --- /dev/null +++ b/Console.Data.SqlServer/Console.Data.SqlServer/appsettings.json @@ -0,0 +1,24 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null + } +} \ No newline at end of file diff --git a/Console.Data.SqlServer/Dockerfile b/Console.Data.SqlServer/Dockerfile new file mode 100644 index 00000000..1f2cd851 --- /dev/null +++ b/Console.Data.SqlServer/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Data.SqlServer.dll"] \ No newline at end of file diff --git a/Console.Data.SqlServer/README.md b/Console.Data.SqlServer/README.md new file mode 100644 index 00000000..cdae2e12 --- /dev/null +++ b/Console.Data.SqlServer/README.md @@ -0,0 +1,198 @@ +# Console.Data.SqlServer + +> _Nano Console application with sql server data._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This example demonstrates how various parts of Nano data work together. All data configuration and registration have been completed, and classes have been implemented +for the data parts, including **[Data Models](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-models)**, **[Data Mappings](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-mappings)**, +and the **[Data Context](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#data-context)**. + +The worker creates and retreives a `Example` entity using the Nano **[Data Repository](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data/README.md#repositories)**. Run the +application and observe the console output generated by the `ExampleWorker`. + +> 📖 Learn more about **[Nano.Data.SqlServer](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Data.SqlServer/README.md#nanodatamysql)**. + +## Registration +The following data provider has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoData(); +}) +... +``` + +Also, an initial migration has been added to the project. + +```powershell +dotnet ef migrations add Initial --project Console.Data.SqlServer +``` + +## Configuration +Configured the application with the necessary data setup. + +```json +"Data": { + "BatchSize": 25, + "BulkBatchSize": 500, + "BulkBatchDelay": 1000, + "QueryRetryCount": 0, + "StartupAction": "None", + "UseLazyLoading": false, + "UseSensitiveDataLogging": false, + "QuerySplittingBehavior": "SingleQuery", + "DefaultCollation": null, + "ConnectionString": null, + "Repository": { + "UseAutoSave": true, + "QueryIncludeDepth": 4 + }, + "Identity": null, + "ConnectionPool": null, + "HealthCheck": null +} +``` + +...and `appsettings.Development.json` + +```json +"Data": { + "StartupAction": "Migrate", + "ConnectionString": "Server=host.docker.internal,1433;Database=nanoDb;User Id=sa;Password=myPassword_123;Encrypt=False;" +} +``` + +## Docker Compose +Added Sql Server as a service dependency in `docker-compose.yml`. + +```yaml +services: + console.data.sqlserver: + depends_on: + - database + + database: + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - 1433:1433 + networks: + - network + environment: + SA_PASSWORD: myPassword_123 + ACCEPT_EULA: Y + MSSQL_PID: Developer +``` + +## Kubernetes +Added the `%SERVICE_NAME%-secret` for the connectionstring to the `cronjob.yaml`. + +```json +spec: + jobTemplate: + spec: + template: + spec: + containers: + env: + - name: Data__ConnectionString + valueFrom: + secretKeyRef: + name: %SERVICE_NAME%-sql-auth-secret + key: data-connectionstring +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + SQL_NAME: nanoDb + SQL_USER: api-data-sqlserver-user + SQL_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_NANO_DB_PASSWORD || secrets.STAGING_SQL_NANO_DB_PASSWORD }} + SQL_ADMIN_PASSWORD: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_SQL_ADMIN_PASSWORD || secrets.STAGING_SQL_ADMIN_PASSWORD }} +``` + +Additionally, this step has been added to ensure database migrations are applied, and the application database user has been created before the application is deployed. + +```yaml +- name: Database Migration & User + shell: pwsh + run: | + $env:SQL_HOST = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].fullyQualifiedDomainName" -o tsv; + $env:SQL_PORT = "1433" + $env:SQL_ADMIN_USER = az sql server list -g $env:AZURE_GROUP_DATABASE --query "[0].administratorLogin" -o tsv; + $env:SQL_MIGRATION_CONNECTIONSTRING = "Server=$env:SQL_HOST,$env:SQL_PORT;Database=$env:SQL_NAME;User Id=$env:SQL_ADMIN_USER;Password=$env:SQL_ADMIN_PASSWORD;Encrypt=True;TrustServerCertificate=True;"; + + dotnet ef database update ` + --no-build ` + --startup-project $env:APP_NAME ` + --connection "$env:SQL_MIGRATION_CONNECTIONSTRING"; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + apt-get update + apt-get install -y mssql-tools unixodbc-dev + + $loginExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.server_principals WHERE name = '$env:SQL_USER';" + + if ($loginExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d master ` + -Q "CREATE LOGIN [$env:SQL_USER] WITH PASSWORD = '$env:SQL_PASSWORD';" + }; + + $userExists = sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -h -1 ` + -Q "SET NOCOUNT ON; SELECT COUNT(*) FROM sys.database_principals WHERE name = '$env:SQL_USER';" + + if ($userExists -eq 0) + { + sqlcmd ` + -S "$env:SQL_HOST,$env:SQL_PORT" ` + -U $env:SQL_ADMIN_USER ` + -P $env:SQL_ADMIN_PASSWORD ` + -d $env:SQL_NAME ` + -Q "CREATE USER [$env:SQL_USER] FOR LOGIN [$env:SQL_USER]; + ALTER ROLE db_datareader ADD MEMBER [$env:SQL_USER]; + ALTER ROLE db_datawriter ADD MEMBER [$env:SQL_USER];" + }; +``` + +Last, an additional template has been added to the deployment for storing the application connectionstring in a Kuberntes secret. diff --git a/Console.Eventing.RabbitMq/.docker/docker-compose.dcproj b/Console.Eventing.RabbitMq/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Eventing.RabbitMq/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/.docker/docker-compose.yml b/Console.Eventing.RabbitMq/.docker/docker-compose.yml new file mode 100644 index 00000000..466eb2f3 --- /dev/null +++ b/Console.Eventing.RabbitMq/.docker/docker-compose.yml @@ -0,0 +1,31 @@ +services: + console.eventing.rabbitmq: + image: console.eventing.rabbitmq + restart: on-failure + build: + context: ../Console.Eventing.RabbitMq + dockerfile: "Dockerfile.Local" + depends_on: + - eventing + networks: + - network + + eventing: + image: rabbitmq:management + hostname: rabbitmq + ports: + - 5671:5671 + - 5672:5672 + - 15671:15671 + - 15672:15672 + networks: + - network + environment: + RABBITMQ_DEFAULT_USER: rabbitmq_user + RABBITMQ_DEFAULT_PASS: password + RABBITMQ_DEFAULT_VHOST: / + +networks: + network: + name: network + external: true diff --git a/Console.Eventing.RabbitMq/.dockerignore b/Console.Eventing.RabbitMq/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Eventing.RabbitMq/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Eventing.RabbitMq/.github/config/slack.yml b/Console.Eventing.RabbitMq/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Eventing.RabbitMq/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml b/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..0233cbe7 --- /dev/null +++ b/Console.Eventing.RabbitMq/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Eventing.RabbitMq + IMAGE_NAME: console.eventing.rabbitmq + SERVICE_NAME: console-eventing-rabbitmq + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Eventing.RabbitMq/.gitignore b/Console.Eventing.RabbitMq/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Eventing.RabbitMq/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/.kubernetes/configmap.yaml b/Console.Eventing.RabbitMq/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Eventing.RabbitMq/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml b/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..44ab91fe --- /dev/null +++ b/Console.Eventing.RabbitMq/.kubernetes/cronjob.yaml @@ -0,0 +1,62 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + env: + - name: Eventing__Credentials__Id + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: username + - name: Eventing__Credentials__Secret + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: password + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret diff --git a/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Properties/DoNotParallelize.cs b/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Tests.Console.Eventing.RabbitMq.csproj b/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Tests.Console.Eventing.RabbitMq.csproj new file mode 100644 index 00000000..acdf942c --- /dev/null +++ b/Console.Eventing.RabbitMq/.tests/Tests.Console.Eventing.RabbitMq/Tests.Console.Eventing.RabbitMq.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.sln b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.sln new file mode 100644 index 00000000..a2017d3b --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.sln @@ -0,0 +1,137 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Eventing.RabbitMq", "Console.Eventing.RabbitMq\Console.Eventing.RabbitMq.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Eventing.RabbitMq", ".tests\Tests.Console.Eventing.RabbitMq\Tests.Console.Eventing.RabbitMq.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing", "..\..\Nano.Library\Nano.Eventing\Nano.Eventing.csproj", "{A8E623BC-70EA-3CC8-AFAD-797F006C4A41}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.RabbitMq", "..\..\Nano.Library\Nano.Eventing.RabbitMq\Nano.Eventing.RabbitMq.csproj", "{0789C863-B371-F968-B9C9-5F3CF3DD9897}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41}.Release|Any CPU.Build.0 = Release|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0789C863-B371-F968-B9C9-5F3CF3DD9897}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {A8E623BC-70EA-3CC8-AFAD-797F006C4A41} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0789C863-B371-F968-B9C9-5F3CF3DD9897} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.csproj b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.csproj new file mode 100644 index 00000000..5b475559 --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq.csproj @@ -0,0 +1,49 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Dockerfile.Local b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Dockerfile.Local new file mode 100644 index 00000000..0cda920a --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/EventingHandler.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/EventingHandler.cs new file mode 100644 index 00000000..4fc890f1 --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/EventingHandler.cs @@ -0,0 +1,26 @@ +using System.Threading; +using System.Threading.Tasks; +using Console.Eventing.RabbitMq.Eventing.Models; +using Nano.Eventing.Abstractions; + +namespace Console.Eventing.RabbitMq.Eventing; + +/// +/// Eventing Handler. +/// +public class EventingHandler : BaseEventHandler +{ + /// + /// Callback. + /// + /// The . + /// Whether the event is retrying. + /// The . + /// Nothing. + public override async Task CallbackAsync(EventModel @event, bool isRetrying, CancellationToken cancellationToken = default) + { + await Task.CompletedTask; + + System.Console.WriteLine(@event.Text); + } +} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/Models/EventModel.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/Models/EventModel.cs new file mode 100644 index 00000000..7e7cd89b --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Eventing/Models/EventModel.cs @@ -0,0 +1,12 @@ +namespace Console.Eventing.RabbitMq.Eventing.Models; + +/// +/// Event Model. +/// +public class EventModel +{ + /// + /// Text. + /// + public virtual string? Text { get; set; } +} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Program.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Program.cs new file mode 100644 index 00000000..d5cafa47 --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Console; +using Nano.Eventing.Extensions; +using Nano.Eventing.RabbitMq; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoEventing(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..ee41940e --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Eventing.RabbitMq")] \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Workers/ExampleWorker.cs b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Workers/ExampleWorker.cs new file mode 100644 index 00000000..0ecea4f0 --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/Workers/ExampleWorker.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using Nano.Eventing.Abstractions; +using System.Threading; +using System.Threading.Tasks; +using Console.Eventing.RabbitMq.Eventing.Models; + +namespace Console.Eventing.RabbitMq.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IEventing eventing) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await eventing + .PublishAsync(new EventModel + { + Text = "Testing eventing" + }, cancellationToken: cancellationToken); + + await Task.Delay(500, cancellationToken); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Development.json b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Development.json new file mode 100644 index 00000000..b1f4b0f1 --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Eventing": { + "Credentials": { + "Id": "rabbitmq_user", + "Secret": "password" + } + } +} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Production.json b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Staging.json b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.json b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.json new file mode 100644 index 00000000..647f1497 --- /dev/null +++ b/Console.Eventing.RabbitMq/Console.Eventing.RabbitMq/appsettings.json @@ -0,0 +1,18 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Eventing": { + "Host": "rabbitmq", + "VHost": "/", + "Port": 5672, + "UseSsl": false, + "Timeout": "00:00:30", + "Heartbeat": 60, + "PrefetchCount": 50, + "Credentials": { + "Id": null, + "Secret": null + } + } +} \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/Dockerfile b/Console.Eventing.RabbitMq/Dockerfile new file mode 100644 index 00000000..986cf2d4 --- /dev/null +++ b/Console.Eventing.RabbitMq/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Eventing.RabbitMq.dll"] \ No newline at end of file diff --git a/Console.Eventing.RabbitMq/README.md b/Console.Eventing.RabbitMq/README.md new file mode 100644 index 00000000..d0a49baa --- /dev/null +++ b/Console.Eventing.RabbitMq/README.md @@ -0,0 +1,126 @@ +# Console.Eventing.RabbitMq + +> _Nano console application with rabbitmq eventing._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +Although event handling is not typically associated with console applications, there are valid scenarios where event-driven architecture makes sense +in a console worker. For example, a worker might retrieve data from a database or an API client and publish events for distributed processing across services. + +Run the application and observe how an `EventModel` instance is published by the `ExampleWorker` and handled by the `EventingHandler`. + +Check the console output for the message: `Testing eventing`. +This message is written by the `EventHandler` when the event is successfully received. + +You can access the RabbitMQ management interface here: **[http://localhost:15672](http://localhost:15672)**. From there, you can monitor the messages +being published and consumed in real time. + +> 📖 Learn more about **[Nano.Eventing.RabbitMq](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Eventing.RabbitMq/README.md#nanoeventingrabbitmq)**. + +## Registration +The following eventing provider has been registered using `ConfigureServices(...)` in `Program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoEventing(); +}) +... +``` + +## Configuration +Configured the application `appsettings.json` with the necessary eventing setup. + +```json +"Eventing": { + "Host": "rabbitmq", + "VHost": "/", + "Port": 5672, + "UseSsl": false, + "Timeout": "00:00:30", + "Heartbeat": 60, + "PrefetchCount": 50, + "Credentials": { + "Id": "rabbitmq_user", + "Secret": "password" + } +} +``` + +...and the `appsettings.Development.json` eventing configuration. + +```json +"Eventing": { + "Credentials": { + "Id": "rabbitmq_user", + "Secret": "password" + } +} +``` + +## Docker Compose +Added RabbitMQ as a service dependency in `docker-compose.yml`. + +```yaml +services: + api.eventing.rabbitmq: + depends_on: + - eventing + + eventing: + image: rabbitmq:management + hostname: rabbitmq + ports: + - 5671:5671 + - 5672:5672 + - 15671:15671 + - 15672:15672 + networks: + - network + environment: + RABBITMQ_DEFAULT_USER: rabbitmq_user + RABBITMQ_DEFAULT_PASS: password + RABBITMQ_DEFAULT_VHOST: "/" + +``` +## Kubernetes +Added the `rabbitmq` secret for password to the `cronjob.yaml`. + +```json +spec: + template: + spec: + containers: + env: + - name: Eventing__Credentials__Id + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: username + envFrom: + - name: Eventing__Credentials__Secret + valueFrom: + secretKeyRef: + name: rabbitmq-auth + key: password +``` + +> ⚠️ The `rabbitmq` secret is created alongside the **[Nano Azure Kubernetes Eventing](https://github.com/Nano-Core/Nano.Azure.Kubernetes/tree/master/Nano.Azure.Kubernetes.RabbitMQ)** diff --git a/Console.ExceptionHandling/.docker/docker-compose.dcproj b/Console.ExceptionHandling/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.ExceptionHandling/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.ExceptionHandling/.docker/docker-compose.yml b/Console.ExceptionHandling/.docker/docker-compose.yml new file mode 100644 index 00000000..25043431 --- /dev/null +++ b/Console.ExceptionHandling/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.exceptionhandling: + image: console.exceptionhandling + restart: on-failure + build: + context: ../Console.ExceptionHandling + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.ExceptionHandling/.dockerignore b/Console.ExceptionHandling/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.ExceptionHandling/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.ExceptionHandling/.github/config/slack.yml b/Console.ExceptionHandling/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.ExceptionHandling/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml b/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..0ad7df32 --- /dev/null +++ b/Console.ExceptionHandling/.github/workflows/build-and-deploy.yml @@ -0,0 +1,150 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.ExceptionHandling + IMAGE_NAME: console.exceptionhandling + SERVICE_NAME: console-exceptionhandling + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.ExceptionHandling/.gitignore b/Console.ExceptionHandling/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.ExceptionHandling/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.ExceptionHandling/.kubernetes/configmap.yaml b/Console.ExceptionHandling/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.ExceptionHandling/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.ExceptionHandling/.kubernetes/cronjob.yaml b/Console.ExceptionHandling/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.ExceptionHandling/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Properties/DoNotParallelize.cs b/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Tests.Console.ExceptionHandling.csproj b/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Tests.Console.ExceptionHandling.csproj new file mode 100644 index 00000000..1e70f784 --- /dev/null +++ b/Console.ExceptionHandling/.tests/Tests.Console.ExceptionHandling/Tests.Console.ExceptionHandling.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.ExceptionHandling/Console.ExceptionHandling.sln b/Console.ExceptionHandling/Console.ExceptionHandling.sln new file mode 100644 index 00000000..01421e59 --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 d18.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.ExceptionHandling", "Console.ExceptionHandling\Console.ExceptionHandling.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.ExceptionHandling", ".tests\Tests.Console.ExceptionHandling\Tests.Console.ExceptionHandling.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Console.ExceptionHandling.csproj b/Console.ExceptionHandling/Console.ExceptionHandling/Console.ExceptionHandling.csproj new file mode 100644 index 00000000..e0dfc0c1 --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/Console.ExceptionHandling.csproj @@ -0,0 +1,43 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Dockerfile.Local b/Console.ExceptionHandling/Console.ExceptionHandling/Dockerfile.Local new file mode 100644 index 00000000..9a65fa1d --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.ExceptionHandling.dll"] \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Program.cs b/Console.ExceptionHandling/Console.ExceptionHandling/Program.cs new file mode 100644 index 00000000..1aa684bd --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Console; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Properties/InternalsVisibleTo.cs b/Console.ExceptionHandling/Console.ExceptionHandling/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..cbd1f000 --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.ExceptionHandling")] \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/Workers/ExampleWorker.cs b/Console.ExceptionHandling/Console.ExceptionHandling/Workers/ExampleWorker.cs new file mode 100644 index 00000000..4c23274e --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/Workers/ExampleWorker.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; + +namespace Console.ExceptionHandling.Workers; + +/// +/// Example Worker. +/// +/// The . +public class ExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.CompletedTask; + + throw new Exception("Example Worker Exception..."); + } +} \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Development.json b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Production.json b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Staging.json b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.json b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.json new file mode 100644 index 00000000..c7b251f2 --- /dev/null +++ b/Console.ExceptionHandling/Console.ExceptionHandling/appsettings.json @@ -0,0 +1,5 @@ +{ + "App": { + "Version": "1.0.0.0" + } +} \ No newline at end of file diff --git a/Console.ExceptionHandling/Dockerfile b/Console.ExceptionHandling/Dockerfile new file mode 100644 index 00000000..e7bc637d --- /dev/null +++ b/Console.ExceptionHandling/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.ExceptionHandling.dll"] \ No newline at end of file diff --git a/Console.ExceptionHandling/README.md b/Console.ExceptionHandling/README.md new file mode 100644 index 00000000..bff2a21a --- /dev/null +++ b/Console.ExceptionHandling/README.md @@ -0,0 +1,23 @@ +# Console.ExceptionHandling + +> _Nano Console application showing exception handling._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates exception handling for a console application. + +Run the application and observe the `Exception` being thrown in the worker `OnStartAsync()`. + +> 📖 Learn more about **[Nano Exception Handling](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Console/README.md#exception-handling)**. diff --git a/Console.Localization/.docker/docker-compose.dcproj b/Console.Localization/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Localization/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Localization/.docker/docker-compose.yml b/Console.Localization/.docker/docker-compose.yml new file mode 100644 index 00000000..26aa2436 --- /dev/null +++ b/Console.Localization/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.localization: + image: console.localization + restart: on-failure + build: + context: ../Console.Localization + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.Localization/.dockerignore b/Console.Localization/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Localization/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Localization/.github/config/slack.yml b/Console.Localization/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Localization/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Localization/.github/workflows/build-and-deploy.yml b/Console.Localization/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..aacf89be --- /dev/null +++ b/Console.Localization/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Localization + IMAGE_NAME: console.localization + SERVICE_NAME: console-localization + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Localization/.gitignore b/Console.Localization/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Localization/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Localization/.kubernetes/configmap.yaml b/Console.Localization/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Localization/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Localization/.kubernetes/cronjob.yaml b/Console.Localization/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.Localization/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Localization/.tests/Tests.Console.Localization/Properties/DoNotParallelize.cs b/Console.Localization/.tests/Tests.Console.Localization/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Localization/.tests/Tests.Console.Localization/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Localization/.tests/Tests.Console.Localization/Tests.Console.Localization.csproj b/Console.Localization/.tests/Tests.Console.Localization/Tests.Console.Localization.csproj new file mode 100644 index 00000000..90bb12ff --- /dev/null +++ b/Console.Localization/.tests/Tests.Console.Localization/Tests.Console.Localization.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Localization/Console.Localization.sln b/Console.Localization/Console.Localization.sln new file mode 100644 index 00000000..a82eed0d --- /dev/null +++ b/Console.Localization/Console.Localization.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 d18.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Localization", "Console.Localization\Console.Localization.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Localization", ".tests\Tests.Console.Localization\Tests.Console.Localization.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Localization/Console.Localization/Console.Localization.csproj b/Console.Localization/Console.Localization/Console.Localization.csproj new file mode 100644 index 00000000..e0dfc0c1 --- /dev/null +++ b/Console.Localization/Console.Localization/Console.Localization.csproj @@ -0,0 +1,43 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Localization/Console.Localization/Dockerfile.Local b/Console.Localization/Console.Localization/Dockerfile.Local new file mode 100644 index 00000000..643806eb --- /dev/null +++ b/Console.Localization/Console.Localization/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Localization.dll"] \ No newline at end of file diff --git a/Console.Localization/Console.Localization/Program.cs b/Console.Localization/Console.Localization/Program.cs new file mode 100644 index 00000000..1aa684bd --- /dev/null +++ b/Console.Localization/Console.Localization/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Console; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Localization/Console.Localization/Properties/InternalsVisibleTo.cs b/Console.Localization/Console.Localization/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..ad479afc --- /dev/null +++ b/Console.Localization/Console.Localization/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Localization")] \ No newline at end of file diff --git a/Console.Localization/Console.Localization/Workers/ExampleWorker.cs b/Console.Localization/Console.Localization/Workers/ExampleWorker.cs new file mode 100644 index 00000000..2c0f3a21 --- /dev/null +++ b/Console.Localization/Console.Localization/Workers/ExampleWorker.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using System; +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; + +namespace Console.Localization.Workers; + +/// +/// Example Worker. +/// +/// The . +public class ExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.CompletedTask; + + System.Console.WriteLine($"Culture: {CultureInfo.CurrentCulture.Name}"); + System.Console.WriteLine($"DateTime: {DateTime.Now}"); + System.Console.WriteLine($"Long date: {DateTime.Now:D}"); + System.Console.WriteLine($"Number: {1234567.89.ToString(CultureInfo.InvariantCulture)}"); + System.Console.WriteLine($"Currency: {1234.56m:C}"); + System.Console.WriteLine($"Percent: {0.256:P}"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Localization/Console.Localization/appsettings.Development.json b/Console.Localization/Console.Localization/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Localization/Console.Localization/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Localization/Console.Localization/appsettings.Production.json b/Console.Localization/Console.Localization/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Localization/Console.Localization/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Localization/Console.Localization/appsettings.Staging.json b/Console.Localization/Console.Localization/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Localization/Console.Localization/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Localization/Console.Localization/appsettings.json b/Console.Localization/Console.Localization/appsettings.json new file mode 100644 index 00000000..80cf620a --- /dev/null +++ b/Console.Localization/Console.Localization/appsettings.json @@ -0,0 +1,8 @@ +{ + "App": { + "Version": "1.0.0.0", + "Localization": { + "DefaultCulture": "fr-FR" + } + } +} \ No newline at end of file diff --git a/Console.Localization/Dockerfile b/Console.Localization/Dockerfile new file mode 100644 index 00000000..82e9da78 --- /dev/null +++ b/Console.Localization/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Localization.dll"] \ No newline at end of file diff --git a/Console.Localization/README.md b/Console.Localization/README.md new file mode 100644 index 00000000..676793bd --- /dev/null +++ b/Console.Localization/README.md @@ -0,0 +1,32 @@ +# Console.Localization + +> _Nano Console application with localization configured._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates configuring `Localization` in a Nano console application. + +Run the application and observe how the configured localization is used when printing out `DateTimeOffset.Now`. + +> 📖 Learn more about **[Nano Console Localization](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Console/README.md#localization)**. + +## Configuration +```json +"App": { + "Localization": { + "DefaultCulture": "fr-FR" + } +} +``` \ No newline at end of file diff --git a/Console.Logging.Log4Net/.docker/docker-compose.dcproj b/Console.Logging.Log4Net/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Logging.Log4Net/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Logging.Log4Net/.docker/docker-compose.yml b/Console.Logging.Log4Net/.docker/docker-compose.yml new file mode 100644 index 00000000..61a250c9 --- /dev/null +++ b/Console.Logging.Log4Net/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.logging.log4net: + image: console.logging.log4net + restart: on-failure + build: + context: ../Console.Logging.Log4Net + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.Logging.Log4Net/.dockerignore b/Console.Logging.Log4Net/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Logging.Log4Net/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Logging.Log4Net/.github/config/slack.yml b/Console.Logging.Log4Net/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Logging.Log4Net/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml b/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..4dc97976 --- /dev/null +++ b/Console.Logging.Log4Net/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Logging.Log4Net + IMAGE_NAME: console.logging.log4net + SERVICE_NAME: console-logging-log4net + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Logging.Log4Net/.gitignore b/Console.Logging.Log4Net/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Logging.Log4Net/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Logging.Log4Net/.kubernetes/configmap.yaml b/Console.Logging.Log4Net/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Logging.Log4Net/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Logging.Log4Net/.kubernetes/cronjob.yaml b/Console.Logging.Log4Net/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.Logging.Log4Net/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Properties/DoNotParallelize.cs b/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Tests.Console.Logging.Log4Net.csproj b/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Tests.Console.Logging.Log4Net.csproj new file mode 100644 index 00000000..e4d56afc --- /dev/null +++ b/Console.Logging.Log4Net/.tests/Tests.Console.Logging.Log4Net/Tests.Console.Logging.Log4Net.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net.sln b/Console.Logging.Log4Net/Console.Logging.Log4Net.sln new file mode 100644 index 00000000..32e304d3 --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net.sln @@ -0,0 +1,137 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Logging.Log4Net", "Console.Logging.Log4Net\Console.Logging.Log4Net.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Logging.Log4Net", ".tests\Tests.Console.Logging.Log4Net\Tests.Console.Logging.Log4Net.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Log4Net", "..\..\Nano.Library\Nano.Logging.Log4Net\Nano.Logging.Log4Net.csproj", "{EF1890AC-F965-E660-35B6-4E181BEB3ED8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.Build.0 = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {EF1890AC-F965-E660-35B6-4E181BEB3ED8} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {68215D72-880F-683C-BFEE-ED19A425A5F2} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Console.Logging.Log4Net.csproj b/Console.Logging.Log4Net/Console.Logging.Log4Net/Console.Logging.Log4Net.csproj new file mode 100644 index 00000000..15fccebd --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/Console.Logging.Log4Net.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Dockerfile.Local b/Console.Logging.Log4Net/Console.Logging.Log4Net/Dockerfile.Local new file mode 100644 index 00000000..f57f1262 --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Program.cs b/Console.Logging.Log4Net/Console.Logging.Log4Net/Program.cs new file mode 100644 index 00000000..3e6a977b --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Console; +using Nano.Logging.Extensions; +using Nano.Logging.Log4Net; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoLogging(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Properties/InternalsVisibleTo.cs b/Console.Logging.Log4Net/Console.Logging.Log4Net/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..2713a2c2 --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Logging.Log4Net")] \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/Workers/ExampleWorker.cs b/Console.Logging.Log4Net/Console.Logging.Log4Net/Workers/ExampleWorker.cs new file mode 100644 index 00000000..ef9929db --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/Workers/ExampleWorker.cs @@ -0,0 +1,30 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; + +namespace Console.Logging.Log4Net.Workers; + +/// +/// Example Worker. +/// +/// The . +public class ExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.Delay(500, cancellationToken); + + this.logger + .LogWarning("warning from worker"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Development.json b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Production.json b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Staging.json b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.json b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.json new file mode 100644 index 00000000..251db522 --- /dev/null +++ b/Console.Logging.Log4Net/Console.Logging.Log4Net/appsettings.json @@ -0,0 +1,14 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] + } +} \ No newline at end of file diff --git a/Console.Logging.Log4Net/Dockerfile b/Console.Logging.Log4Net/Dockerfile new file mode 100644 index 00000000..af52ba2e --- /dev/null +++ b/Console.Logging.Log4Net/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Logging.Log4Net.dll"] \ No newline at end of file diff --git a/Console.Logging.Log4Net/README.md b/Console.Logging.Log4Net/README.md new file mode 100644 index 00000000..908b42a8 --- /dev/null +++ b/Console.Logging.Log4Net/README.md @@ -0,0 +1,54 @@ +# Console.Logging.Log4Net + +> _Nano Console application with Log4Net logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates logging with Log4Net for a console application. + +Run the application and observe how `ExampleWorker` logs a warning to the console. Also note the `LogLevelOverrides` configuration, +where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. + +> 📖 Learn more about **[Nano.Logging.Log4Net](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Log4Net/README.md#nanologginglog4net)**. + +## Registration +The following logging has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoLogging(); +}) +... +``` + +## Configuration +Configured the application with the necessary logging setup. + +```json +"Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] +} +``` diff --git a/Console.Logging.Microsoft/.docker/docker-compose.dcproj b/Console.Logging.Microsoft/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Logging.Microsoft/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Logging.Microsoft/.docker/docker-compose.yml b/Console.Logging.Microsoft/.docker/docker-compose.yml new file mode 100644 index 00000000..98d18205 --- /dev/null +++ b/Console.Logging.Microsoft/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.logging.microsoft: + image: console.logging.microsoft + restart: on-failure + build: + context: ../Console.Logging.Microsoft + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.Logging.Microsoft/.dockerignore b/Console.Logging.Microsoft/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Logging.Microsoft/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Logging.Microsoft/.github/config/slack.yml b/Console.Logging.Microsoft/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Logging.Microsoft/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml b/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..1c105bb3 --- /dev/null +++ b/Console.Logging.Microsoft/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Logging.Microsoft + IMAGE_NAME: console.logging.microsoft + SERVICE_NAME: console-logging-microsoft + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Logging.Microsoft/.gitignore b/Console.Logging.Microsoft/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Logging.Microsoft/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Logging.Microsoft/.kubernetes/configmap.yaml b/Console.Logging.Microsoft/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Logging.Microsoft/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Logging.Microsoft/.kubernetes/cronjob.yaml b/Console.Logging.Microsoft/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.Logging.Microsoft/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Properties/DoNotParallelize.cs b/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Tests.Console.Logging.Microsoft.csproj b/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Tests.Console.Logging.Microsoft.csproj new file mode 100644 index 00000000..15c8c082 --- /dev/null +++ b/Console.Logging.Microsoft/.tests/Tests.Console.Logging.Microsoft/Tests.Console.Logging.Microsoft.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft.sln b/Console.Logging.Microsoft/Console.Logging.Microsoft.sln new file mode 100644 index 00000000..42f29852 --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft.sln @@ -0,0 +1,137 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Logging.Microsoft", "Console.Logging.Microsoft\Console.Logging.Microsoft.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Logging.Microsoft", ".tests\Tests.Console.Logging.Microsoft\Tests.Console.Logging.Microsoft.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Microsoft", "..\..\Nano.Library\Nano.Logging.Microsoft\Nano.Logging.Microsoft.csproj", "{EF1890AC-F965-E660-35B6-4E181BEB3ED8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.Build.0 = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {EF1890AC-F965-E660-35B6-4E181BEB3ED8} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {68215D72-880F-683C-BFEE-ED19A425A5F2} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Console.Logging.Microsoft.csproj b/Console.Logging.Microsoft/Console.Logging.Microsoft/Console.Logging.Microsoft.csproj new file mode 100644 index 00000000..ae5f47a1 --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/Console.Logging.Microsoft.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Dockerfile.Local b/Console.Logging.Microsoft/Console.Logging.Microsoft/Dockerfile.Local new file mode 100644 index 00000000..2d0c8eaa --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Program.cs b/Console.Logging.Microsoft/Console.Logging.Microsoft/Program.cs new file mode 100644 index 00000000..7643b9eb --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Console; +using Nano.Logging.Extensions; +using Nano.Logging.Microsoft; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoLogging(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Properties/InternalsVisibleTo.cs b/Console.Logging.Microsoft/Console.Logging.Microsoft/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..09d934ee --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Logging.Microsoft")] \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/Workers/ExampleWorker.cs b/Console.Logging.Microsoft/Console.Logging.Microsoft/Workers/ExampleWorker.cs new file mode 100644 index 00000000..0916532a --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/Workers/ExampleWorker.cs @@ -0,0 +1,30 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; + +namespace Console.Logging.Microsoft.Workers; + +/// +/// Example Worker. +/// +/// The . +public class ExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.Delay(500, cancellationToken); + + this.logger + .LogWarning("warning from worker"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Development.json b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Production.json b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Staging.json b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.json b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.json new file mode 100644 index 00000000..251db522 --- /dev/null +++ b/Console.Logging.Microsoft/Console.Logging.Microsoft/appsettings.json @@ -0,0 +1,14 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] + } +} \ No newline at end of file diff --git a/Console.Logging.Microsoft/Dockerfile b/Console.Logging.Microsoft/Dockerfile new file mode 100644 index 00000000..d3277f49 --- /dev/null +++ b/Console.Logging.Microsoft/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Logging.Microsoft.dll"] \ No newline at end of file diff --git a/Console.Logging.Microsoft/README.md b/Console.Logging.Microsoft/README.md new file mode 100644 index 00000000..c110bd22 --- /dev/null +++ b/Console.Logging.Microsoft/README.md @@ -0,0 +1,54 @@ +# Console.Logging.Microsoft + +> _Nano Console application with Microsoft logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates logging with Microsoft for a console application. + +Run the application and observe how `ExampleWorker` logs a warning to the console. Also note the `LogLevelOverrides` configuration, +where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. + +> 📖 Learn more about **[Nano.Logging.Microsoft](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Microsoft/README.md#nanologgingmicrosoft)**. + +## Registration +The following logging has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoLogging(); +}) +... +``` + +## Configuration +Configured the application with the necessary logging setup. + +```json +"Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] +} +``` \ No newline at end of file diff --git a/Console.Logging.NLog/.docker/docker-compose.dcproj b/Console.Logging.NLog/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Logging.NLog/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Logging.NLog/.docker/docker-compose.yml b/Console.Logging.NLog/.docker/docker-compose.yml new file mode 100644 index 00000000..aeb82911 --- /dev/null +++ b/Console.Logging.NLog/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.logging.nlog: + image: console.logging.nlog + restart: on-failure + build: + context: ../Console.Logging.NLog + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.Logging.NLog/.dockerignore b/Console.Logging.NLog/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Logging.NLog/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Logging.NLog/.github/config/slack.yml b/Console.Logging.NLog/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Logging.NLog/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Logging.NLog/.github/workflows/build-and-deploy.yml b/Console.Logging.NLog/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..01436524 --- /dev/null +++ b/Console.Logging.NLog/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Logging.NLog + IMAGE_NAME: console.logging.nlog + SERVICE_NAME: console-logging-nlog + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Logging.NLog/.gitignore b/Console.Logging.NLog/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Logging.NLog/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Logging.NLog/.kubernetes/configmap.yaml b/Console.Logging.NLog/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Logging.NLog/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Logging.NLog/.kubernetes/cronjob.yaml b/Console.Logging.NLog/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.Logging.NLog/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Properties/DoNotParallelize.cs b/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Tests.Console.Logging.NLog.csproj b/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Tests.Console.Logging.NLog.csproj new file mode 100644 index 00000000..d2958fd9 --- /dev/null +++ b/Console.Logging.NLog/.tests/Tests.Console.Logging.NLog/Tests.Console.Logging.NLog.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Logging.NLog/Console.Logging.NLog.sln b/Console.Logging.NLog/Console.Logging.NLog.sln new file mode 100644 index 00000000..04ef1b18 --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog.sln @@ -0,0 +1,137 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Logging.NLog", "Console.Logging.NLog\Console.Logging.NLog.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Logging.NLog", ".tests\Tests.Console.Logging.NLog\Tests.Console.Logging.NLog.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.NLog", "..\..\Nano.Library\Nano.Logging.NLog\Nano.Logging.NLog.csproj", "{EF1890AC-F965-E660-35B6-4E181BEB3ED8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.Build.0 = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {EF1890AC-F965-E660-35B6-4E181BEB3ED8} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {68215D72-880F-683C-BFEE-ED19A425A5F2} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Logging.NLog/Console.Logging.NLog/Console.Logging.NLog.csproj b/Console.Logging.NLog/Console.Logging.NLog/Console.Logging.NLog.csproj new file mode 100644 index 00000000..8fafd5d3 --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/Console.Logging.NLog.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/Dockerfile.Local b/Console.Logging.NLog/Console.Logging.NLog/Dockerfile.Local new file mode 100644 index 00000000..26d2db27 --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Logging.NLog.dll"] \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/Program.cs b/Console.Logging.NLog/Console.Logging.NLog/Program.cs new file mode 100644 index 00000000..00862263 --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Console; +using Nano.Logging.Extensions; +using Nano.Logging.NLog; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoLogging(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/Properties/InternalsVisibleTo.cs b/Console.Logging.NLog/Console.Logging.NLog/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..f8803c57 --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Logging.NLog")] \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/Workers/ExampleWorker.cs b/Console.Logging.NLog/Console.Logging.NLog/Workers/ExampleWorker.cs new file mode 100644 index 00000000..185dfecb --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/Workers/ExampleWorker.cs @@ -0,0 +1,30 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; + +namespace Console.Logging.NLog.Workers; + +/// +/// Example Worker. +/// +/// The . +public class ExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.Delay(500, cancellationToken); + + this.logger + .LogWarning("warning from worker"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Development.json b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Production.json b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/appsettings.Staging.json b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.NLog/Console.Logging.NLog/appsettings.json b/Console.Logging.NLog/Console.Logging.NLog/appsettings.json new file mode 100644 index 00000000..251db522 --- /dev/null +++ b/Console.Logging.NLog/Console.Logging.NLog/appsettings.json @@ -0,0 +1,14 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] + } +} \ No newline at end of file diff --git a/Console.Logging.NLog/Dockerfile b/Console.Logging.NLog/Dockerfile new file mode 100644 index 00000000..8eac5ef1 --- /dev/null +++ b/Console.Logging.NLog/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Logging.NLog.dll"] \ No newline at end of file diff --git a/Console.Logging.NLog/README.md b/Console.Logging.NLog/README.md new file mode 100644 index 00000000..f5724807 --- /dev/null +++ b/Console.Logging.NLog/README.md @@ -0,0 +1,54 @@ +# Console.Logging.NLog + +> _Nano Console application with NLog logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates logging with NLog for a console application. + +Run the application and observe how `ExampleWorker` logs a warning to the console. Also note the `LogLevelOverrides` configuration, +where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. + +> 📖 Learn more about **[Nano.Logging.NLog](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.NLog/README.md#nanologgingnlog)**. + +## Registration +The following logging has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoLogging(); +}) +... +``` + +## Configuration +Configured the application with the necessary logging setup. + +```json +"Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] +} +``` diff --git a/Console.Logging.Serilog/.docker/docker-compose.dcproj b/Console.Logging.Serilog/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Logging.Serilog/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Logging.Serilog/.docker/docker-compose.yml b/Console.Logging.Serilog/.docker/docker-compose.yml new file mode 100644 index 00000000..b28a3fb6 --- /dev/null +++ b/Console.Logging.Serilog/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.logging.serilog: + image: console.logging.serilog + restart: on-failure + build: + context: ../Console.Logging.Serilog + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.Logging.Serilog/.dockerignore b/Console.Logging.Serilog/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Logging.Serilog/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Logging.Serilog/.github/config/slack.yml b/Console.Logging.Serilog/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Logging.Serilog/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml b/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..fb5412b5 --- /dev/null +++ b/Console.Logging.Serilog/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Logging.Serilog + IMAGE_NAME: console.logging.serilog + SERVICE_NAME: console-logging-serilog + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Logging.Serilog/.gitignore b/Console.Logging.Serilog/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Logging.Serilog/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Logging.Serilog/.kubernetes/configmap.yaml b/Console.Logging.Serilog/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Logging.Serilog/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Logging.Serilog/.kubernetes/cronjob.yaml b/Console.Logging.Serilog/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.Logging.Serilog/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Properties/DoNotParallelize.cs b/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Tests.Console.Logging.Serilog.csproj b/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Tests.Console.Logging.Serilog.csproj new file mode 100644 index 00000000..b7c58339 --- /dev/null +++ b/Console.Logging.Serilog/.tests/Tests.Console.Logging.Serilog/Tests.Console.Logging.Serilog.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Logging.Serilog/Console.Logging.Serilog.sln b/Console.Logging.Serilog/Console.Logging.Serilog.sln new file mode 100644 index 00000000..19f00bf2 --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog.sln @@ -0,0 +1,137 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Logging.Serilog", "Console.Logging.Serilog\Console.Logging.Serilog.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Logging.Serilog", ".tests\Tests.Console.Logging.Serilog\Tests.Console.Logging.Serilog.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Serilog", "..\..\Nano.Library\Nano.Logging.Serilog\Nano.Logging.Serilog.csproj", "{EF1890AC-F965-E660-35B6-4E181BEB3ED8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging", "..\..\Nano.Library\Nano.Logging\Nano.Logging.csproj", "{68215D72-880F-683C-BFEE-ED19A425A5F2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF1890AC-F965-E660-35B6-4E181BEB3ED8}.Release|Any CPU.Build.0 = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68215D72-880F-683C-BFEE-ED19A425A5F2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {EF1890AC-F965-E660-35B6-4E181BEB3ED8} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {68215D72-880F-683C-BFEE-ED19A425A5F2} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Console.Logging.Serilog.csproj b/Console.Logging.Serilog/Console.Logging.Serilog/Console.Logging.Serilog.csproj new file mode 100644 index 00000000..903628f2 --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/Console.Logging.Serilog.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Dockerfile.Local b/Console.Logging.Serilog/Console.Logging.Serilog/Dockerfile.Local new file mode 100644 index 00000000..8f3057fd --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Program.cs b/Console.Logging.Serilog/Console.Logging.Serilog/Program.cs new file mode 100644 index 00000000..69e4e373 --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Console; +using Nano.Logging.Extensions; +using Nano.Logging.Serilog; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoLogging(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Properties/InternalsVisibleTo.cs b/Console.Logging.Serilog/Console.Logging.Serilog/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..bb321253 --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Logging.Serilog")] \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/Workers/ExampleWorker.cs b/Console.Logging.Serilog/Console.Logging.Serilog/Workers/ExampleWorker.cs new file mode 100644 index 00000000..5bf16abe --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/Workers/ExampleWorker.cs @@ -0,0 +1,30 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; + +namespace Console.Logging.Serilog.Workers; + +/// +/// Example Worker. +/// +/// The . +public class ExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.Delay(500, cancellationToken); + + this.logger + .LogWarning("warning from worker"); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Development.json b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Production.json b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Staging.json b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.json b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.json new file mode 100644 index 00000000..251db522 --- /dev/null +++ b/Console.Logging.Serilog/Console.Logging.Serilog/appsettings.json @@ -0,0 +1,14 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] + } +} \ No newline at end of file diff --git a/Console.Logging.Serilog/Dockerfile b/Console.Logging.Serilog/Dockerfile new file mode 100644 index 00000000..f7e09b43 --- /dev/null +++ b/Console.Logging.Serilog/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Logging.Serilog.dll"] \ No newline at end of file diff --git a/Console.Logging.Serilog/README.md b/Console.Logging.Serilog/README.md new file mode 100644 index 00000000..df5621de --- /dev/null +++ b/Console.Logging.Serilog/README.md @@ -0,0 +1,54 @@ +# Console.Logging.Serilog + +> _Nano Console application with Serilog logging._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates logging with Serilog for a console application. + +Run the application and observe how `ExampleWorker` logs a warning to the console. Also note the `LogLevelOverrides` configuration, +where logs under the `Microsoft` namespace are set to `Warning`, which suppresses several informational messages during application startup. + +> 📖 Learn more about **[Nano.Logging.Serilog](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Logging.Serilog/README.md#nanologgingserilog)**. + +## Registration +The following logging has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(services => +{ + services + .AddNanoLogging(); +}) +... +``` + +## Configuration +Configured the application with the necessary logging setup. + +```json +"Logging": { + "LogLevel": "Information", + "LogLevelOverrides": [ + { + "Namespace": "Microsoft", + "LogLevel": "Warning" + } + ] +} +``` diff --git a/Console.StartupTasks/.docker/docker-compose.dcproj b/Console.StartupTasks/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.StartupTasks/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.StartupTasks/.docker/docker-compose.yml b/Console.StartupTasks/.docker/docker-compose.yml new file mode 100644 index 00000000..b1212011 --- /dev/null +++ b/Console.StartupTasks/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.startuptasks: + image: console.startuptasks + restart: on-failure + build: + context: ../Console.StartupTasks + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.StartupTasks/.dockerignore b/Console.StartupTasks/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.StartupTasks/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.StartupTasks/.github/config/slack.yml b/Console.StartupTasks/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.StartupTasks/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.StartupTasks/.github/workflows/build-and-deploy.yml b/Console.StartupTasks/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..80e559e4 --- /dev/null +++ b/Console.StartupTasks/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.StartupTasks + IMAGE_NAME: console.startuptasks + SERVICE_NAME: console-startuptasks + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.StartupTasks/.gitignore b/Console.StartupTasks/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.StartupTasks/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.StartupTasks/.kubernetes/configmap.yaml b/Console.StartupTasks/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.StartupTasks/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.StartupTasks/.kubernetes/cronjob.yaml b/Console.StartupTasks/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..32007e73 --- /dev/null +++ b/Console.StartupTasks/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret \ No newline at end of file diff --git a/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Properties/DoNotParallelize.cs b/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Tests.Console.StartupTasks.csproj b/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Tests.Console.StartupTasks.csproj new file mode 100644 index 00000000..c78ddc11 --- /dev/null +++ b/Console.StartupTasks/.tests/Tests.Console.StartupTasks/Tests.Console.StartupTasks.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.StartupTasks/Console.StartupTasks.sln b/Console.StartupTasks/Console.StartupTasks.sln new file mode 100644 index 00000000..80c9db04 --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 d18.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.StartupTasks", "Console.StartupTasks\Console.StartupTasks.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.StartupTasks", ".tests\Tests.Console.StartupTasks\Tests.Console.StartupTasks.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.StartupTasks/Console.StartupTasks/Console.StartupTasks.csproj b/Console.StartupTasks/Console.StartupTasks/Console.StartupTasks.csproj new file mode 100644 index 00000000..e0dfc0c1 --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/Console.StartupTasks.csproj @@ -0,0 +1,43 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Dockerfile.Local b/Console.StartupTasks/Console.StartupTasks/Dockerfile.Local new file mode 100644 index 00000000..afbd78eb --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.StartupTasks.dll"] \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Program.cs b/Console.StartupTasks/Console.StartupTasks/Program.cs new file mode 100644 index 00000000..1aa684bd --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Console; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Properties/InternalsVisibleTo.cs b/Console.StartupTasks/Console.StartupTasks/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..d6deb4b6 --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.StartupTasks")] \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Startup/ExampleStartupTask.cs b/Console.StartupTasks/Console.StartupTasks/Startup/ExampleStartupTask.cs new file mode 100644 index 00000000..830990c2 --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/Startup/ExampleStartupTask.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.StartUp; + +namespace Console.StartupTasks.Startup; + +/// +/// Example Startup Task. +/// +/// The . +public class ExampleStartupTask(ILogger logger) : BaseStartupTask(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Startup Task Started..."); + + await Task.Delay(2000, cancellationToken); + + System.Console.WriteLine(DateTime.UtcNow); + + System.Console.WriteLine("Example Startup Task Completed..."); + } +} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/Workers/ExampleWorker.cs b/Console.StartupTasks/Console.StartupTasks/Workers/ExampleWorker.cs new file mode 100644 index 00000000..4c0633f7 --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/Workers/ExampleWorker.cs @@ -0,0 +1,30 @@ +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Console.StartupTasks.Workers; + +/// +/// Example Worker. +/// +/// The . +public class ExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.Delay(100, cancellationToken); + + System.Console.WriteLine(DateTime.UtcNow); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/appsettings.Development.json b/Console.StartupTasks/Console.StartupTasks/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/appsettings.Production.json b/Console.StartupTasks/Console.StartupTasks/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/appsettings.Staging.json b/Console.StartupTasks/Console.StartupTasks/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.StartupTasks/Console.StartupTasks/appsettings.json b/Console.StartupTasks/Console.StartupTasks/appsettings.json new file mode 100644 index 00000000..c7b251f2 --- /dev/null +++ b/Console.StartupTasks/Console.StartupTasks/appsettings.json @@ -0,0 +1,5 @@ +{ + "App": { + "Version": "1.0.0.0" + } +} \ No newline at end of file diff --git a/Console.StartupTasks/Dockerfile b/Console.StartupTasks/Dockerfile new file mode 100644 index 00000000..c0ce3143 --- /dev/null +++ b/Console.StartupTasks/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.StartupTasks.dll"] \ No newline at end of file diff --git a/Console.StartupTasks/README.md b/Console.StartupTasks/README.md new file mode 100644 index 00000000..8277099a --- /dev/null +++ b/Console.StartupTasks/README.md @@ -0,0 +1,24 @@ +# Console.StartupTasks + +> _Nano Console application with startup tasks._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates a waiting worker that delays its execution until all registered startup tasks have completed, +ensuring that the console application only runs after initialization is finished. + +Run the application and observe how both example workers wait for the startup tasks to complete before executing their logic. + +> 📖 Learn more about **[Nano Startup Tasks](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App/README.md#startup-tasks)**. diff --git a/Console.Storage.Azure/.docker/docker-compose.dcproj b/Console.Storage.Azure/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Storage.Azure/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Storage.Azure/.docker/docker-compose.yml b/Console.Storage.Azure/.docker/docker-compose.yml new file mode 100644 index 00000000..0750c797 --- /dev/null +++ b/Console.Storage.Azure/.docker/docker-compose.yml @@ -0,0 +1,16 @@ +services: + console.storage.azure: + image: console.storage.azure + restart: on-failure + build: + context: ../Console.Storage.Azure + dockerfile: "Dockerfile.Local" + networks: + - network + volumes: + - ./bin/nano-storage-azure:/mnt/nano-storage-azure + +networks: + network: + name: network + external: true diff --git a/Console.Storage.Azure/.dockerignore b/Console.Storage.Azure/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Storage.Azure/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Storage.Azure/.github/config/slack.yml b/Console.Storage.Azure/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Storage.Azure/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Storage.Azure/.github/workflows/build-and-deploy.yml b/Console.Storage.Azure/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..140653e8 --- /dev/null +++ b/Console.Storage.Azure/.github/workflows/build-and-deploy.yml @@ -0,0 +1,209 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Storage.Azure + IMAGE_NAME: console.storage.azure + SERVICE_NAME: console-storage-azure + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_GROUP_STORAGE: ${{ vars.AZURE_RESOURCE_GROUP_STORAGE }} + AZURE_GROUP_BACKUP: ${{ vars.AZURE_RESOURCE_GROUP_BACKUP }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + STORAGE_SIZE: 1000 + STORAGE_SHARE_NAME: nano-storage-azure + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Create Fileshare + shell: pwsh + run: | + $env:STORAGE_ACCOUNT_NAME = az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].name -o tsv; + + $env:FILE_SHARE_EXISTS = az storage share-rm exists ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --query exists; + + if ($env:FILE_SHARE_EXISTS -eq "false") + { + az storage share-rm create ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --access-tier TransactionOptimized ` + --quota $env:STORAGE_SIZE; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $env:BACKUP_VAULT_NAME = az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; + + az backup protection enable-for-azurefileshare ` + -g $env:AZURE_GROUP_BACKUP ` + -v $env:BACKUP_VAULT_NAME ` + -p $env:STORAGE_ACCOUNT_NAME-backup-policy ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --azure-file-share $env:STORAGE_SHARE_NAME; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/storage-pv.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pv.tmp.yaml; + kubectl apply -f .kubernetes/storage-pv.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/storage-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pvc.tmp.yaml; + kubectl apply -f .kubernetes/storage-pvc.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Storage.Azure/.gitignore b/Console.Storage.Azure/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Storage.Azure/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Storage.Azure/.kubernetes/configmap.yaml b/Console.Storage.Azure/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Storage.Azure/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Storage.Azure/.kubernetes/cronjob.yaml b/Console.Storage.Azure/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..ea18271f --- /dev/null +++ b/Console.Storage.Azure/.kubernetes/cronjob.yaml @@ -0,0 +1,64 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + restartPolicy: OnFailure + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-azurefile-pvc + - name: tmp + emptyDir: {} + imagePullSecrets: + - name: ghcr-pull-secret + + diff --git a/Console.Storage.Azure/.kubernetes/storage-pv.yaml b/Console.Storage.Azure/.kubernetes/storage-pv.yaml new file mode 100644 index 00000000..b6281fb8 --- /dev/null +++ b/Console.Storage.Azure/.kubernetes/storage-pv.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: %SERVICE_NAME%-azurefile-pv +spec: + capacity: + storage: %STORAGE_SIZE% + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + storageClassName: azurefile-static + csi: + driver: file.csi.azure.com + volumeHandle: %STORAGE_SHARE_NAME% + volumeAttributes: + shareName: %STORAGE_SHARE_NAME% + storageAccount: %STORAGE_ACCOUNT_NAME% \ No newline at end of file diff --git a/Console.Storage.Azure/.kubernetes/storage-pvc.yaml b/Console.Storage.Azure/.kubernetes/storage-pvc.yaml new file mode 100644 index 00000000..252c7857 --- /dev/null +++ b/Console.Storage.Azure/.kubernetes/storage-pvc.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %SERVICE_NAME%-azurefile-pvc + namespace: %KUBERNETES_NAMESPACE% +spec: + accessModes: + - ReadWriteMany + storageClassName: azurefile-static + resources: + requests: + storage: %STORAGE_SIZE% \ No newline at end of file diff --git a/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Properties/DoNotParallelize.cs b/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Tests.Console.Storage.Azure.csproj b/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Tests.Console.Storage.Azure.csproj new file mode 100644 index 00000000..bf29923e --- /dev/null +++ b/Console.Storage.Azure/.tests/Tests.Console.Storage.Azure/Tests.Console.Storage.Azure.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Storage.Azure/Console.Storage.Azure.sln b/Console.Storage.Azure/Console.Storage.Azure.sln new file mode 100644 index 00000000..63806ffa --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure.sln @@ -0,0 +1,139 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Storage.Azure", "Console.Storage.Azure\Console.Storage.Azure.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + .kubernetes\storage-pv.yaml = .kubernetes\storage-pv.yaml + .kubernetes\storage-pvc.yaml = .kubernetes\storage-pvc.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Storage.Azure", ".tests\Tests.Console.Storage.Azure\Tests.Console.Storage.Azure.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage", "..\..\Nano.Library\Nano.Storage\Nano.Storage.csproj", "{24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Azure", "..\..\Nano.Library\Nano.Storage.Azure\Nano.Storage.Azure.csproj", "{5DD1307B-16FA-5171-54CA-4F37F2638022}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.Build.0 = Release|Any CPU + {5DD1307B-16FA-5171-54CA-4F37F2638022}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5DD1307B-16FA-5171-54CA-4F37F2638022}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5DD1307B-16FA-5171-54CA-4F37F2638022}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5DD1307B-16FA-5171-54CA-4F37F2638022}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {5DD1307B-16FA-5171-54CA-4F37F2638022} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Storage.Azure/Console.Storage.Azure/Console.Storage.Azure.csproj b/Console.Storage.Azure/Console.Storage.Azure/Console.Storage.Azure.csproj new file mode 100644 index 00000000..604c83ea --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/Console.Storage.Azure.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/Dockerfile.Local b/Console.Storage.Azure/Console.Storage.Azure/Dockerfile.Local new file mode 100644 index 00000000..b4edc380 --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Storage.Azure.dll"] \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/Program.cs b/Console.Storage.Azure/Console.Storage.Azure/Program.cs new file mode 100644 index 00000000..f1a5f6c7 --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Console; +using Nano.Storage.Azure; +using Nano.Storage.Extensions; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoStorage(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/Properties/InternalsVisibleTo.cs b/Console.Storage.Azure/Console.Storage.Azure/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..7ec66a84 --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Storage.Azure")] \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/Workers/ExampleWorker.cs b/Console.Storage.Azure/Console.Storage.Azure/Workers/ExampleWorker.cs new file mode 100644 index 00000000..cf04dcb5 --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/Workers/ExampleWorker.cs @@ -0,0 +1,34 @@ +using System.IO; +using System.Text; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using Nano.Storage.Abstractions; +using System.Threading; +using System.Threading.Tasks; + +namespace Console.Storage.Azure.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IPathProvider pathProvider) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + var fileName = Path.GetFileName("test-file.txt"); + var savePath = Path.Combine(pathProvider.Root, fileName); + + await File.WriteAllTextAsync(savePath, "content", Encoding.UTF8, cancellationToken); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Development.json b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Production.json b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/appsettings.Staging.json b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Storage.Azure/Console.Storage.Azure/appsettings.json b/Console.Storage.Azure/Console.Storage.Azure/appsettings.json new file mode 100644 index 00000000..061420b2 --- /dev/null +++ b/Console.Storage.Azure/Console.Storage.Azure/appsettings.json @@ -0,0 +1,8 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Storage": { + "ShareName": "nano-storage-azure" + } +} \ No newline at end of file diff --git a/Console.Storage.Azure/Dockerfile b/Console.Storage.Azure/Dockerfile new file mode 100644 index 00000000..3d7996cd --- /dev/null +++ b/Console.Storage.Azure/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Storage.Azure.dll"] \ No newline at end of file diff --git a/Console.Storage.Azure/README.md b/Console.Storage.Azure/README.md new file mode 100644 index 00000000..3731aeef --- /dev/null +++ b/Console.Storage.Azure/README.md @@ -0,0 +1,134 @@ +# Console.Storage.Azure + +> _Nano Console application with azure storage._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates creating a file and saving it to a mapped file share. +When running locally, files are **NOT** written to the Azure File Share. Instead, Docker mounts a local directory to simulate the file share. +Files are saved in `.docker/bin/`. + +> 📖 Learn more about **[Nano.Storage.Azure](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Storage.Azure/README#nanostorageazure)**. + +## Registration +The following storage has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(x => +{ + x.AddNanoStorage(); +}) +... +``` + +## Configuration +Configured the application with the necessary storage setup. + +```json +"Storage": { + "ShareName": "nano-storage-azure" +} +``` + +## Docker Compose +Mapped the fileshare in `docker-compose.yml`. + +```yaml +docker + volumes: + - ./bin/nano-storage-azure:/mnt/nano-storage-azure +``` + +## Kubernetes +Added two new kubernetes templaets, the `storage-pv.yaml` and `storage-pvc.yaml`. Updated the `cronjob.yaml` mounting the volume. + +```yaml +spec: + template: + spec: + containers: + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-azurefile-pvc + - name: tmp + emptyDir: {} +``` + +## GitHub Actions +Add the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + AZURE_GROUP_BACKUP: ${{ vars.AZURE_BACKUP_RESOURCE_GROUP }} + AZURE_GROUP_STORAGE: ${{ vars.AZURE_STORAGE_RESOURCE_GROUP }} + STORAGE_SIZE: 1000 + STORAGE_SHARE_NAME: nano-storage-azure +``` + +Additionally, this step has been added to ensure the file share is created before the application is deployed. + +```yaml +- name: Create Fileshare + shell: pwsh + run: | + $env:STORAGE_ACCOUNT_NAME = sudo az storage account list -g $env:AZURE_GROUP_STORAGE --query [0].name -o tsv; + + $env:FILE_SHARE_EXISTS = sudo az storage share-rm exists ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --query exists; + + if ($env:FILE_SHARE_EXISTS -eq "false") + { + sudo az storage share-rm create ` + -g $env:AZURE_GROUP_STORAGE ` + -n $env:STORAGE_SHARE_NAME ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --access-tier TransactionOptimized ` + --quota $env:STORAGE_SIZE; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + $env:BACKUP_VAULT_NAME = sudo az backup vault list -g $env:AZURE_GROUP_BACKUP --query [0].name -o tsv; + + sudo az backup protection enable-for-azurefileshare ` + -g $env:AZURE_GROUP_BACKUP ` + -v $env:BACKUP_VAULT_NAME ` + -p $env:STORAGE_ACCOUNT_NAME-backup-policy ` + --storage-account $env:STORAGE_ACCOUNT_NAME ` + --azure-file-share $env:STORAGE_SHARE_NAME; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + } +``` \ No newline at end of file diff --git a/Console.Storage.Local/.docker/docker-compose.dcproj b/Console.Storage.Local/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Storage.Local/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Storage.Local/.docker/docker-compose.yml b/Console.Storage.Local/.docker/docker-compose.yml new file mode 100644 index 00000000..f877ac93 --- /dev/null +++ b/Console.Storage.Local/.docker/docker-compose.yml @@ -0,0 +1,16 @@ +services: + console.storage.local: + image: console.storage.local + restart: on-failure + build: + context: ../Console.Storage.Local + dockerfile: "Dockerfile.Local" + networks: + - network + volumes: + - ./bin/nano-storage-local:/mnt/nano-storage-local + +networks: + network: + name: network + external: true diff --git a/Console.Storage.Local/.dockerignore b/Console.Storage.Local/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Storage.Local/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Storage.Local/.github/config/slack.yml b/Console.Storage.Local/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Storage.Local/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Storage.Local/.github/workflows/build-and-deploy.yml b/Console.Storage.Local/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..236021f4 --- /dev/null +++ b/Console.Storage.Local/.github/workflows/build-and-deploy.yml @@ -0,0 +1,167 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Storage.Local + IMAGE_NAME: console.storage.local + SERVICE_NAME: console-storage-local + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + STORAGE_SIZE: 1000 + STORAGE_SHARE_NAME: nano-storage-local + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/storage-storageclass.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-storageclass.tmp.yaml; + kubectl apply -f .kubernetes/storage-storageclass.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/storage-pvc.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/storage-pvc.tmp.yaml; + kubectl apply -f .kubernetes/storage-pvc.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Storage.Local/.gitignore b/Console.Storage.Local/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Storage.Local/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Storage.Local/.kubernetes/configmap.yaml b/Console.Storage.Local/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Storage.Local/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Storage.Local/.kubernetes/cronjob.yaml b/Console.Storage.Local/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..0604cbad --- /dev/null +++ b/Console.Storage.Local/.kubernetes/cronjob.yaml @@ -0,0 +1,63 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + restartPolicy: OnFailure + volumes: + - name: %STORAGE_SHARE_NAME% + emptyDir: {} + - name: tmp + emptyDir: {} + imagePullSecrets: + - name: ghcr-pull-secret + + diff --git a/Console.Storage.Local/.kubernetes/storage-pvc.yaml b/Console.Storage.Local/.kubernetes/storage-pvc.yaml new file mode 100644 index 00000000..2285607e --- /dev/null +++ b/Console.Storage.Local/.kubernetes/storage-pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %SERVICE_NAME%-pvc +spec: + accessModes: + - ReadWriteOnce + storageClassName: %SERVICE_NAME%-storage-class + resources: + requests: + storage: %STORAGE_SIZE% \ No newline at end of file diff --git a/Console.Storage.Local/.kubernetes/storage-storageclass.yaml b/Console.Storage.Local/.kubernetes/storage-storageclass.yaml new file mode 100644 index 00000000..f9ccaa3e --- /dev/null +++ b/Console.Storage.Local/.kubernetes/storage-storageclass.yaml @@ -0,0 +1,10 @@ +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: %SERVICE_NAME%-storage-class +provisioner: kubernetes.io/azure-disk +parameters: + storageaccounttype: Standard_LRS + kind: Managed +reclaimPolicy: Retain +volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Properties/DoNotParallelize.cs b/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Tests.Console.Storage.Local.csproj b/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Tests.Console.Storage.Local.csproj new file mode 100644 index 00000000..e03ab3b2 --- /dev/null +++ b/Console.Storage.Local/.tests/Tests.Console.Storage.Local/Tests.Console.Storage.Local.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Storage.Local/Console.Storage.Local.sln b/Console.Storage.Local/Console.Storage.Local.sln new file mode 100644 index 00000000..53476dc1 --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local.sln @@ -0,0 +1,140 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Storage.Local", "Console.Storage.Local\Console.Storage.Local.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + .kubernetes\pvc.yaml = .kubernetes\pvc.yaml + .kubernetes\storage-pvc.yaml = .kubernetes\storage-pvc.yaml + .kubernetes\storage-storageclass.yaml = .kubernetes\storage-storageclass.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Storage.Local", ".tests\Tests.Console.Storage.Local\Tests.Console.Storage.Local.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage", "..\..\Nano.Library\Nano.Storage\Nano.Storage.csproj", "{24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Local", "..\..\Nano.Library\Nano.Storage.Local\Nano.Storage.Local.csproj", "{6D21CF50-D3ED-4D55-27D9-D426E661A5E4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5}.Release|Any CPU.Build.0 = Release|Any CPU + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {24FF96ED-01BD-FEEC-4ABF-B8C5F58438D5} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6D21CF50-D3ED-4D55-27D9-D426E661A5E4} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Storage.Local/Console.Storage.Local/Console.Storage.Local.csproj b/Console.Storage.Local/Console.Storage.Local/Console.Storage.Local.csproj new file mode 100644 index 00000000..e3e35dff --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/Console.Storage.Local.csproj @@ -0,0 +1,45 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/Dockerfile.Local b/Console.Storage.Local/Console.Storage.Local/Dockerfile.Local new file mode 100644 index 00000000..1839f37a --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Storage.Local.dll"] \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/Program.cs b/Console.Storage.Local/Console.Storage.Local/Program.cs new file mode 100644 index 00000000..04d69c99 --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/Program.cs @@ -0,0 +1,12 @@ +using Nano.App.Console; +using Nano.Storage.Extensions; +using Nano.Storage.Local; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + x.AddNanoStorage(); + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/Properties/InternalsVisibleTo.cs b/Console.Storage.Local/Console.Storage.Local/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..96941052 --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Storage.Local")] \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/Workers/ExampleWorker.cs b/Console.Storage.Local/Console.Storage.Local/Workers/ExampleWorker.cs new file mode 100644 index 00000000..c7e62b7f --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/Workers/ExampleWorker.cs @@ -0,0 +1,34 @@ +using System.IO; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Nano.Storage.Abstractions; + +namespace Console.Storage.Local.Workers; + +/// +/// Example Worker. +/// +/// The . +/// The . +public class ExampleWorker(ILogger logger, IPathProvider pathProvider) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + var fileName = Path.GetFileName("test-file.txt"); + var savePath = Path.Combine(pathProvider.Root, fileName); + + await File.WriteAllTextAsync(savePath, "content", Encoding.UTF8, cancellationToken); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/appsettings.Development.json b/Console.Storage.Local/Console.Storage.Local/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/appsettings.Production.json b/Console.Storage.Local/Console.Storage.Local/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/appsettings.Staging.json b/Console.Storage.Local/Console.Storage.Local/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Storage.Local/Console.Storage.Local/appsettings.json b/Console.Storage.Local/Console.Storage.Local/appsettings.json new file mode 100644 index 00000000..7bce4a53 --- /dev/null +++ b/Console.Storage.Local/Console.Storage.Local/appsettings.json @@ -0,0 +1,8 @@ +{ + "App": { + "Version": "1.0.0.0" + }, + "Storage": { + "ShareName": "nano-storage-local" + } +} \ No newline at end of file diff --git a/Console.Storage.Local/Dockerfile b/Console.Storage.Local/Dockerfile new file mode 100644 index 00000000..5e4b05e8 --- /dev/null +++ b/Console.Storage.Local/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Storage.Local.dll"] \ No newline at end of file diff --git a/Console.Storage.Local/README.md b/Console.Storage.Local/README.md new file mode 100644 index 00000000..f8b0ac76 --- /dev/null +++ b/Console.Storage.Local/README.md @@ -0,0 +1,99 @@ +# Console.Storage.Local + +> _Nano Console application with local storage._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) +* [Registration](#registration) +* [Configuration](#configuration) +* [Docker-compose](#docker-compose) +* [Kubernetes](#kubernetes) +* [GitHub Actions](#github-actions) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates creating a file and saving it to a locally mapped file-share. +Files are saved in `.docker/bin/`. + +> 📖 Learn more about **[Nano.Storage.Local](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.Storage.Local/README#nanostoragelocal)**. + +## Registration +The following storage has been registered using `ConfigureServices(...)` in `program.cs`. + +```csharp +... +.ConfigureServices(x => +{ + x.AddNanoStorage(); +}) +... +``` + +## Configuration +Configured the application with the necessary storage setup. + +```json +"Storage": { + "ShareName": "nano-storage-local" +} +``` + +## Docker Compose +Mapped the fileshare in `docker-compose.yml`. + +```yaml +docker + volumes: + - ./bin/nano-storage-local:/mnt/nano-storage-local +``` + +## Kubernetes +Added two additional kubernetes templates, `storage-storageclass.yaml` and `storage-pvc.yaml`, for dynamically manage and creating the local fileshare. + +Also, updated `cronjob.yaml` adding the volumes and volume mounts. + +```yaml +spec: + template: + spec: + containers: + volumeMounts: + - name: %SERVICE_NAME%-volume + mountPath: /mnt/%STORAGE_SHARE_NAME% + - name: tmp + mountPath: /tmp + volumes: + - name: %SERVICE_NAME%-volume + persistentVolumeClaim: + claimName: %SERVICE_NAME%-pvc + - name: tmp + emptyDir: {} +``` + +## GitHub Actions +Added the following environment variables to the `buid-and-deply.yml`. + +```yaml +env: + STORAGE_SHARE_NAME: nano-storage-local + STORAGE_SIZE: 1000 +``` + +Deployment commands have also been updated to apply each of the new Kubernetes templates. + +```powershell +Get-Content .kubernetes/{resource-name}.yaml ` + | foreach { [Environment]::ExpandEnvironmentVariables($_) } ` + | Set-Content .kubernetes/{resource-name}.tmp.yaml; + +sudo kubectl apply -f .kubernetes/{resource-name}.tmp.yaml; +``` diff --git a/Console.Workers/.docker/docker-compose.dcproj b/Console.Workers/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console.Workers/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console.Workers/.docker/docker-compose.yml b/Console.Workers/.docker/docker-compose.yml new file mode 100644 index 00000000..4c2e3672 --- /dev/null +++ b/Console.Workers/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.workers: + image: console.workers + restart: on-failure + build: + context: ../Console.Workers + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console.Workers/.dockerignore b/Console.Workers/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console.Workers/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console.Workers/.github/config/slack.yml b/Console.Workers/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console.Workers/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console.Workers/.github/workflows/build-and-deploy.yml b/Console.Workers/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..16aadd83 --- /dev/null +++ b/Console.Workers/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Workers + IMAGE_NAME: console.workers + SERVICE_NAME: console-workers + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console.Workers/.gitignore b/Console.Workers/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console.Workers/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console.Workers/.kubernetes/configmap.yaml b/Console.Workers/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console.Workers/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console.Workers/.kubernetes/cronjob.yaml b/Console.Workers/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..503cd99a --- /dev/null +++ b/Console.Workers/.kubernetes/cronjob.yaml @@ -0,0 +1,53 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret + + diff --git a/Console.Workers/.tests/Tests.Console.Workers/Properties/DoNotParallelize.cs b/Console.Workers/.tests/Tests.Console.Workers/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console.Workers/.tests/Tests.Console.Workers/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console.Workers/.tests/Tests.Console.Workers/Tests.Console.Workers.csproj b/Console.Workers/.tests/Tests.Console.Workers/Tests.Console.Workers.csproj new file mode 100644 index 00000000..49d1c17a --- /dev/null +++ b/Console.Workers/.tests/Tests.Console.Workers/Tests.Console.Workers.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console.Workers/Console.Workers.sln b/Console.Workers/Console.Workers.sln new file mode 100644 index 00000000..39e8f574 --- /dev/null +++ b/Console.Workers/Console.Workers.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 d18.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Workers", "Console.Workers\Console.Workers.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Workers", ".tests\Tests.Console.Workers\Tests.Console.Workers.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console.Workers/Console.Workers/Console.Workers.csproj b/Console.Workers/Console.Workers/Console.Workers.csproj new file mode 100644 index 00000000..e0dfc0c1 --- /dev/null +++ b/Console.Workers/Console.Workers/Console.Workers.csproj @@ -0,0 +1,43 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Dockerfile.Local b/Console.Workers/Console.Workers/Dockerfile.Local new file mode 100644 index 00000000..0251200b --- /dev/null +++ b/Console.Workers/Console.Workers/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Workers.dll"] \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Program.cs b/Console.Workers/Console.Workers/Program.cs new file mode 100644 index 00000000..1aa684bd --- /dev/null +++ b/Console.Workers/Console.Workers/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Console; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Properties/InternalsVisibleTo.cs b/Console.Workers/Console.Workers/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..91f2d69f --- /dev/null +++ b/Console.Workers/Console.Workers/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Workers")] \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Workers/AnotherExampleWorker.cs b/Console.Workers/Console.Workers/Workers/AnotherExampleWorker.cs new file mode 100644 index 00000000..8ac7540d --- /dev/null +++ b/Console.Workers/Console.Workers/Workers/AnotherExampleWorker.cs @@ -0,0 +1,27 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; + +namespace Console.Workers.Workers; + +/// +/// Another Example Worker. +/// +/// The . +public class AnotherExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Another Example Worker Started..."); + + await Task.Delay(1000, cancellationToken); + + System.Console.WriteLine("Another Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/Workers/ExampleWorker.cs b/Console.Workers/Console.Workers/Workers/ExampleWorker.cs new file mode 100644 index 00000000..06b56cbc --- /dev/null +++ b/Console.Workers/Console.Workers/Workers/ExampleWorker.cs @@ -0,0 +1,27 @@ +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Nano.App.Console.Workers; + +namespace Console.Workers.Workers; + +/// +/// Example Worker. +/// +/// The . +public class ExampleWorker(ILogger logger) : BaseWorker(logger) +{ + /// + /// Example On Start. + /// + /// The . + /// A . + public override async Task OnStartAsync(CancellationToken cancellationToken = default) + { + System.Console.WriteLine("Example Worker Started..."); + + await Task.Delay(500, cancellationToken); + + System.Console.WriteLine("Example Worker Completed..."); + } +} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/appsettings.Development.json b/Console.Workers/Console.Workers/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Workers/Console.Workers/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/appsettings.Production.json b/Console.Workers/Console.Workers/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Workers/Console.Workers/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/appsettings.Staging.json b/Console.Workers/Console.Workers/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console.Workers/Console.Workers/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console.Workers/Console.Workers/appsettings.json b/Console.Workers/Console.Workers/appsettings.json new file mode 100644 index 00000000..c7b251f2 --- /dev/null +++ b/Console.Workers/Console.Workers/appsettings.json @@ -0,0 +1,5 @@ +{ + "App": { + "Version": "1.0.0.0" + } +} \ No newline at end of file diff --git a/Console.Workers/Dockerfile b/Console.Workers/Dockerfile new file mode 100644 index 00000000..1a4df7b5 --- /dev/null +++ b/Console.Workers/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Workers.dll"] \ No newline at end of file diff --git a/Console.Workers/README.md b/Console.Workers/README.md new file mode 100644 index 00000000..6f891e3c --- /dev/null +++ b/Console.Workers/README.md @@ -0,0 +1,23 @@ +# Console.Workers + +> _Nano Console application with workers._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application builds on **[Console.Blank](https://github.com/Nano-Core/Nano.Lessons/tree/master/Console._Blank)**. + +This application demonstrates multiple workers for a console application. + +Run the application and observe how both implemented workers execute, and see the console output generated by the `ExampleWorker` and `AnotherExampleWorker`. + +> 📖 Learn more about **[Nano Console Workers](https://github.com/Nano-Core/Nano.Library/tree/master/Nano.App.Console/README.md#console-workers)**. diff --git a/Console._Blank/.docker/docker-compose.dcproj b/Console._Blank/.docker/docker-compose.dcproj new file mode 100644 index 00000000..acca666d --- /dev/null +++ b/Console._Blank/.docker/docker-compose.dcproj @@ -0,0 +1,15 @@ + + + + 2.1 + Linux + 557a0c48-da6a-4d7c-8668-94f08a390f4b + false + + + Nano.Template.Console + + + + + \ No newline at end of file diff --git a/Console._Blank/.docker/docker-compose.yml b/Console._Blank/.docker/docker-compose.yml new file mode 100644 index 00000000..de34d983 --- /dev/null +++ b/Console._Blank/.docker/docker-compose.yml @@ -0,0 +1,14 @@ +services: + console.blank: + image: console.blank + restart: on-failure + build: + context: ../Console.Blank + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + external: true diff --git a/Console._Blank/.dockerignore b/Console._Blank/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Console._Blank/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Console._Blank/.github/config/slack.yml b/Console._Blank/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Console._Blank/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Console._Blank/.github/workflows/build-and-deploy.yml b/Console._Blank/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..4a3c8b1d --- /dev/null +++ b/Console._Blank/.github/workflows/build-and-deploy.yml @@ -0,0 +1,151 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Console.Blank + IMAGE_NAME: console.blank + SERVICE_NAME: console-blank + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_HISTORY_COUNT: 4 + KUBERNETES_MEMORY_REQUEST: 256Mi + KUBERNETES_MEMORY_LIMIT: 768Mi + KUBERNETES_CPU_REQUEST: 50m + KUBERNETES_CPU_LIMIT: 150m + KUBERNETES_CRONJOB_SCHEDULE: "0 * * * *" + DOTNET_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/cronjob.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/cronjob.tmp.yaml; + kubectl apply -f .kubernetes/cronjob.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} diff --git a/Console._Blank/.gitignore b/Console._Blank/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Console._Blank/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Console._Blank/.kubernetes/configmap.yaml b/Console._Blank/.kubernetes/configmap.yaml new file mode 100644 index 00000000..f2293b0f --- /dev/null +++ b/Console._Blank/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + DOTNET_ENVIRONMENT: %DOTNET_ENVIRONMENT% \ No newline at end of file diff --git a/Console._Blank/.kubernetes/cronjob.yaml b/Console._Blank/.kubernetes/cronjob.yaml new file mode 100644 index 00000000..71ff564c --- /dev/null +++ b/Console._Blank/.kubernetes/cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: %SERVICE_NAME% +spec: + schedule: %KUBERNETES_CRONJOB_SCHEDULE% + concurrencyPolicy: Forbid + failedJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + successfulJobsHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + jobTemplate: + metadata: + labels: + app: %SERVICE_NAME% + spec: + template: + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + restartPolicy: OnFailure + imagePullSecrets: + - name: ghcr-pull-secret diff --git a/Console._Blank/.tests/Tests.Console.Blank/Properties/DoNotParallelize.cs b/Console._Blank/.tests/Tests.Console.Blank/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Console._Blank/.tests/Tests.Console.Blank/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Console._Blank/.tests/Tests.Console.Blank/Tests.Console.Blank.csproj b/Console._Blank/.tests/Tests.Console.Blank/Tests.Console.Blank.csproj new file mode 100644 index 00000000..f6d022d5 --- /dev/null +++ b/Console._Blank/.tests/Tests.Console.Blank/Tests.Console.Blank.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Console._Blank/Console.Blank.sln b/Console._Blank/Console.Blank.sln new file mode 100644 index 00000000..77c0c608 --- /dev/null +++ b/Console._Blank/Console.Blank.sln @@ -0,0 +1,123 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.2.11415.280 d18.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Console.Blank", "Console.Blank\Console.Blank.csproj", "{C563A416-5EDD-4228-B8C7-B5AFEC141466}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\cronjob.yaml = .kubernetes\cronjob.yaml + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Console.Blank", ".tests\Tests.Console.Blank\Tests.Console.Blank.csproj", "{EA602798-C127-44E3-8CBA-27E9BA269EB7}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{06514BD5-76DD-468A-9B0E-398FF055B759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{427D135E-2F7D-4C22-B437-701F2116B068}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{530D085E-BE1C-42C7-9B74-87E791DFA4A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{C5676710-E363-4808-80B5-CE2EB49DFD27}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Console", "..\..\Nano.Library\Nano.App.Console\Nano.App.Console.csproj", "{0095379A-0921-C55A-AFC3-64C0445A3E2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C563A416-5EDD-4228-B8C7-B5AFEC141466}.Release|Any CPU.Build.0 = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA602798-C127-44E3-8CBA-27E9BA269EB7}.Release|Any CPU.Build.0 = Release|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Debug|Any CPU.Build.0 = Debug|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.ActiveCfg = Release|Any CPU + {427D135E-2F7D-4C22-B437-701F2116B068}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0095379A-0921-C55A-AFC3-64C0445A3E2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {EA602798-C127-44E3-8CBA-27E9BA269EB7} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {427D135E-2F7D-4C22-B437-701F2116B068} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {C5676710-E363-4808-80B5-CE2EB49DFD27} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {EB0103E3-FEDE-4F5B-ADCA-A3AF50A47818} = {530D085E-BE1C-42C7-9B74-87E791DFA4A2} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {06514BD5-76DD-468A-9B0E-398FF055B759} + {0095379A-0921-C55A-AFC3-64C0445A3E2B} = {06514BD5-76DD-468A-9B0E-398FF055B759} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Console._Blank/Console.Blank/Console.Blank.csproj b/Console._Blank/Console.Blank/Console.Blank.csproj new file mode 100644 index 00000000..e0dfc0c1 --- /dev/null +++ b/Console._Blank/Console.Blank/Console.Blank.csproj @@ -0,0 +1,43 @@ + + + + net10.0 + + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Console._Blank/Console.Blank/Dockerfile.Local b/Console._Blank/Console.Blank/Dockerfile.Local new file mode 100644 index 00000000..03b8a154 --- /dev/null +++ b/Console._Blank/Console.Blank/Dockerfile.Local @@ -0,0 +1,5 @@ +ARG DOTNET_ASPNET_VERSION="10.0" + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +ENTRYPOINT ["dotnet", "Console.Blank.dll"] \ No newline at end of file diff --git a/Console._Blank/Console.Blank/Program.cs b/Console._Blank/Console.Blank/Program.cs new file mode 100644 index 00000000..2a6b33aa --- /dev/null +++ b/Console._Blank/Console.Blank/Program.cs @@ -0,0 +1,10 @@ +using Nano.App.Console; + +NanoConsoleApplication + .ConfigureApp(args) + .ConfigureServices(x => + { + // Blank + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Console._Blank/Console.Blank/Properties/InternalsVisibleTo.cs b/Console._Blank/Console.Blank/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..9c0858cc --- /dev/null +++ b/Console._Blank/Console.Blank/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Console.Blank")] \ No newline at end of file diff --git a/Console._Blank/Console.Blank/appsettings.Development.json b/Console._Blank/Console.Blank/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console._Blank/Console.Blank/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console._Blank/Console.Blank/appsettings.Production.json b/Console._Blank/Console.Blank/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console._Blank/Console.Blank/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console._Blank/Console.Blank/appsettings.Staging.json b/Console._Blank/Console.Blank/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Console._Blank/Console.Blank/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Console._Blank/Console.Blank/appsettings.json b/Console._Blank/Console.Blank/appsettings.json new file mode 100644 index 00000000..c7b251f2 --- /dev/null +++ b/Console._Blank/Console.Blank/appsettings.json @@ -0,0 +1,5 @@ +{ + "App": { + "Version": "1.0.0.0" + } +} \ No newline at end of file diff --git a/Console._Blank/Dockerfile b/Console._Blank/Dockerfile new file mode 100644 index 00000000..16d41684 --- /dev/null +++ b/Console._Blank/Dockerfile @@ -0,0 +1,31 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENTRYPOINT ["dotnet", "Console.Blank.dll"] \ No newline at end of file diff --git a/Console._Blank/README.md b/Console._Blank/README.md new file mode 100644 index 00000000..79f9bd9b --- /dev/null +++ b/Console._Blank/README.md @@ -0,0 +1,26 @@ +# Console.Blank + +> _Minimal (blank) Nano Console application._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application represents the most minimal (blank) Nano Console application setup, and has no console workers implemented. + +Its purpose is to demonstrate the required boilerplate, file structure, and general configuration needed to create a Nano Console application. +The application itself is intentionally minimal and does not do anything meaningful. Instead, it serves as a baseline from which all other Console lessons and examples +are built. + +It is recommended to review this application first to understand how a Nano Console application is generally structured and to become familiar with the +purpose of the core building blocks used in the boilerplate. + +> 📖 Learn more about **[Nano Solution Composition](https://github.com/Nano-Core/Nano.Library#solution-composition)**. diff --git a/Nano.Lessons.sln.cmd b/Nano.Lessons.sln.cmd new file mode 100644 index 00000000..0c8adc5e --- /dev/null +++ b/Nano.Lessons.sln.cmd @@ -0,0 +1,2 @@ +@echo off +start "" devenv.exe "%~dp0" \ No newline at end of file diff --git a/Web._Blank/.docker/docker-compose.dcproj b/Web._Blank/.docker/docker-compose.dcproj new file mode 100644 index 00000000..d9e8e500 --- /dev/null +++ b/Web._Blank/.docker/docker-compose.dcproj @@ -0,0 +1,13 @@ + + + + 2.1 + Linux + false + http://localhost:{ServicePort}/docs + $(ProjectName) + + + + + \ No newline at end of file diff --git a/Web._Blank/.docker/docker-compose.yml b/Web._Blank/.docker/docker-compose.yml new file mode 100644 index 00000000..54765d3c --- /dev/null +++ b/Web._Blank/.docker/docker-compose.yml @@ -0,0 +1,17 @@ +services: + web.blank: + image: web.blank + hostname: web-blank + restart: on-failure + ports: + - 8080:8080 + build: + context: ../Web.Blank + dockerfile: "Dockerfile.Local" + networks: + - network + +networks: + network: + name: network + driver: bridge diff --git a/Web._Blank/.dockerignore b/Web._Blank/.dockerignore new file mode 100644 index 00000000..e694ae21 --- /dev/null +++ b/Web._Blank/.dockerignore @@ -0,0 +1,12 @@ +.dockerignore +.env +.git +.gitignore +.vs +.vscode +docker-compose.yml +docker-compose.*.yml +*/bin +*/obj +!obj/Docker/publish/* +!obj/Docker/empty/ diff --git a/Web._Blank/.github/config/slack.yml b/Web._Blank/.github/config/slack.yml new file mode 100644 index 00000000..3592affb --- /dev/null +++ b/Web._Blank/.github/config/slack.yml @@ -0,0 +1,18 @@ +username: GitHub Actions +icon_url: https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png + +pretext: "{{icon jobStatus}} *<{{repositoryUrl}}|{{repositoryName}}>* <{{workflowRunUrl}}|`#{{runNumber}}`> triggered via {{eventName}} by ** for branch <{{refUrl}}|`{{ref}}`>." + +text: | + {{#if payload.commits}} + *Commits* + {{#each payload.commits}} + <{{this.url}}|`{{truncate this.id 8}}`> - {{this.message}} + {{/each}} + {{/if}} + +footer: >- + <{{repositoryUrl}}|{{repositoryName}}> #{{runNumber}} + +fallback: |- + [GitHub] {{workflow}} #{{runNumber}} {{jobName}} is {{jobStatus}} diff --git a/Web._Blank/.github/workflows/build-and-deploy.yml b/Web._Blank/.github/workflows/build-and-deploy.yml new file mode 100644 index 00000000..ec5d0318 --- /dev/null +++ b/Web._Blank/.github/workflows/build-and-deploy.yml @@ -0,0 +1,179 @@ +name: Build And Deploy +on: + pull_request: + branches: + - master + push: + branches: + - master +env: + APP_NAME: Web.Blank + IMAGE_NAME: web.blank + SERVICE_NAME: web-blank + VERSION: '${{ vars.VERSION }}.${{ github.run_number }}.${{ github.run_attempt }}' + DOTNET_SDK_VERSION: 10.0 + DOTNET_ASPNET_VERSION: 10.0 + AZURE_GROUP_KUBERNETES: ${{ vars.AZURE_RESOURCE_GROUP_KUBERNETES }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_SUBSCRIPTION_ID: ${{ github.ref == 'refs/heads/master' && secrets.PRODUCTION_AZURE_SUBSCRIPTION_ID || secrets.STAGING_AZURE_SUBSCRIPTION_ID }} + NUGET_HOST: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json + NUGET_USERNAME: ${{ github.actor }} + NUGET_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_HOST: ghcr.io + CONTAINER_REGISTRY_USERNAME: ${{ github.actor }} + CONTAINER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} + CONTAINER_REGISTRY_SOURCE_LABEL: https://github.com/${{ github.repository }} + KUBERNETES_NODEPOOL_COMPUTE: cpu + KUBERNETES_NAMESPACE: apps + KUBERNETES_REPLICA_COUNT: ${{ github.ref == 'refs/heads/master' && 3 || 2 }} + KUBERNETES_REPLICA_COUNT_MAX: ${{ github.ref == 'refs/heads/master' && 8 || 5 }} + KUBERNETES_REPLICA_HISTORY_COUNT: 0 + KUBERNETES_MEMORY_REQUEST: 512Mi + KUBERNETES_MEMORY_LIMIT: 1536Mi + KUBERNETES_MEMORY_SCALING: 180 + KUBERNETES_CPU_REQUEST: 200m + KUBERNETES_CPU_LIMIT: 600m + KUBERNETES_CPU_SCALING: 180 + ASPNETCORE_ENVIRONMENT: ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} +jobs: + build-and-deploy: + runs-on: + - self-hosted + - linux + - ${{ github.ref == 'refs/heads/master' && 'Production' || 'Staging' }} + permissions: + contents: write + packages: write + id-token: write + concurrency: + group: ${{ github.repository }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v6 + + - name: Azure Login + shell: pwsh + run: | + az login --service-principal -u $env:AZURE_CLIENT_ID -p $env:AZURE_CLIENT_SECRET --tenant $env:AZURE_TENANT_ID -o none; + az account set -s $env:AZURE_SUBSCRIPTION_ID -o none; + + $env:KUBERNETES_CLUSTER = az aks list -g $env:AZURE_GROUP_KUBERNETES --query [0].name -o tsv; + az aks get-credentials -g $env:AZURE_GROUP_KUBERNETES -n $env:KUBERNETES_CLUSTER --overwrite -o none; + + - name: Build + shell: pwsh + run: | + dotnet nuget add source $env:NUGET_HOST -n private -u $env:NUGET_USERNAME -p $env:NUGET_PASSWORD --store-password-in-clear-text; + + dotnet build -c Release .\$env:APP_NAME.sln; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Test + shell: pwsh + run: | + dotnet test .\.tests\Tests.$env:APP_NAME\Tests.$env:APP_NAME.csproj; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish Image + shell: pwsh + run: | + $registryHost = $env:CONTAINER_REGISTRY_HOST.ToLower() + $imageLatestTag = $registryHost + "/" + $env:IMAGE_NAME + ":latest"; + $imageVersionTag = $registryHost + "/" + $env:IMAGE_NAME + ":" + $env:VERSION + + docker build ` + -t $imageLatestTag ` + -t $imageVersionTag ` + --build-arg DOTNET_SDK_VERSION=$env:DOTNET_SDK_VERSION ` + --build-arg DOTNET_ASPNET_VERSION=$env:DOTNET_ASPNET_VERSION ` + --build-arg CONTAINER_REGISTRY_SOURCE_LABEL=$env:CONTAINER_REGISTRY_SOURCE_LABEL ` + --build-arg NUGET_HOST=$env:NUGET_HOST ` + --build-arg NUGET_USERNAME=$env:NUGET_USERNAME ` + --build-arg NUGET_PASSWORD=$env:NUGET_PASSWORD ` + ./; + + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + echo $env:CONTAINER_REGISTRY_PASSWORD | docker login $env:CONTAINER_REGISTRY_HOST -u $env:CONTAINER_REGISTRY_USERNAME --password-stdin; + docker push $imageLatestTag; + docker push $imageVersionTag; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Publish NuGet + shell: pwsh + run: | + $nugetProjectModels=$env:APP_NAME + ".Models/" + $env:APP_NAME + ".Models.csproj"; + dotnet pack $nugetProjectModels -c Release --output nupkgs /p:PackageVersion=$env:VERSION --include-symbols --no-build; + dotnet nuget push nupkgs/$env:APP_NAME".Models."$env:VERSION.nupkg -s $env:NUGET_HOST -k $env:NUGET_PASSWORD; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: Kubernetes Deploy + shell: pwsh + run: | + Get-Content .kubernetes/service.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/service.tmp.yaml; + kubectl apply -f .kubernetes/service.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/configmap.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/configmap.tmp.yaml; + kubectl apply -f .kubernetes/configmap.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/deployment.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/deployment.tmp.yaml; + kubectl apply -f .kubernetes/deployment.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + Get-Content .kubernetes/autoscaler.yaml | foreach { [Environment]::ExpandEnvironmentVariables($_) } | Set-Content .kubernetes/autoscaler.tmp.yaml; + kubectl apply -f .kubernetes/autoscaler.tmp.yaml; + if ($LastExitCode -ne 0) + { + throw "error"; + }; + + - name: GitHub Release + if: github.ref == 'refs/heads/master' + uses: ncipollo/release-action@v1 + with: + tag: v${{ env.VERSION }} + name: "Release ${{ env.VERSION }}" + body: | + Version: ${{ env.VERSION }} + Commit: ${{ github.sha }} + artifacts: "nupkgs/*" + token: ${{ secrets.GITHUB_TOKEN }} + draft: false + prerelease: false + + - name: Slack Notification + if: always() + uses: act10ns/slack@v2 + with: + webhook-url: ${{ secrets.SLACK_WEBHOOK }} + config: .github/config/slack.yml + status: ${{ job.status }} + channel: ${{ vars.SLACK_CHANNEL }} \ No newline at end of file diff --git a/Web._Blank/.gitignore b/Web._Blank/.gitignore new file mode 100644 index 00000000..9921fc06 --- /dev/null +++ b/Web._Blank/.gitignore @@ -0,0 +1,11 @@ +.vs +*.user +*.userprefs +*.suo +_ReSharper* +**/bin +**/obj +*.DotSettings.User +packages +.env +**/Properties/launchSettings.json \ No newline at end of file diff --git a/Web._Blank/.kubernetes/autoscaler.yaml b/Web._Blank/.kubernetes/autoscaler.yaml new file mode 100644 index 00000000..95add8ad --- /dev/null +++ b/Web._Blank/.kubernetes/autoscaler.yaml @@ -0,0 +1,25 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: %SERVICE_NAME%-hpa + namespace: %KUBERNETES_NAMESPACE% +spec: + minReplicas: %KUBERNETES_REPLICA_COUNT% + maxReplicas: %KUBERNETES_REPLICA_COUNT_MAX% + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: %SERVICE_NAME% + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: %KUBERNETES_CPU_SCALING% + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: %KUBERNETES_MEMORY_SCALING% diff --git a/Web._Blank/.kubernetes/configmap.yaml b/Web._Blank/.kubernetes/configmap.yaml new file mode 100644 index 00000000..3977c0ee --- /dev/null +++ b/Web._Blank/.kubernetes/configmap.yaml @@ -0,0 +1,8 @@ + apiVersion: v1 +kind: ConfigMap +metadata: + name: %SERVICE_NAME%-config + namespace: %KUBERNETES_NAMESPACE% +data: + App__Version: %VERSION% + ASPNETCORE_ENVIRONMENT: %ASPNETCORE_ENVIRONMENT% diff --git a/Web._Blank/.kubernetes/deployment.yaml b/Web._Blank/.kubernetes/deployment.yaml new file mode 100644 index 00000000..02882c56 --- /dev/null +++ b/Web._Blank/.kubernetes/deployment.yaml @@ -0,0 +1,78 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% + labels: + app: %SERVICE_NAME% +spec: + replicas: %KUBERNETES_REPLICA_COUNT% + revisionHistoryLimit: %KUBERNETES_REPLICA_HISTORY_COUNT% + selector: + matchLabels: + app: %SERVICE_NAME% + template: + metadata: + labels: + app: %SERVICE_NAME% + spec: + automountServiceAccountToken: false + securityContext: + runAsUser: 1000 + runAsGroup: 2000 + fsGroup: 2000 + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app: %SERVICE_NAME% + nodeSelector: + nodepool.compute: %KUBERNETES_NODEPOOL_COMPUTE% + kubernetes.io/os: linux + containers: + - name: %SERVICE_NAME% + image: %CONTAINER_REGISTRY_HOST%/%IMAGE_NAME%:%VERSION% + ports: + - containerPort: 8080 + imagePullPolicy: Always + envFrom: + - configMapRef: + name: %SERVICE_NAME%-config + resources: + requests: + memory: %KUBERNETES_MEMORY_REQUEST% + cpu: %KUBERNETES_CPU_REQUEST% + limits: + memory: %KUBERNETES_MEMORY_LIMIT% + cpu: %KUBERNETES_CPU_LIMIT% + securityContext: + privileged: false + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 2000 + capabilities: + drop: + - ALL + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 10 + initialDelaySeconds: 30 + timeoutSeconds: 2 + readinessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + periodSeconds: 5 + initialDelaySeconds: 20 + timeoutSeconds: 2 + imagePullSecrets: + - name: ghcr-pull-secret + diff --git a/Web._Blank/.kubernetes/service.yaml b/Web._Blank/.kubernetes/service.yaml new file mode 100644 index 00000000..2d8e20b2 --- /dev/null +++ b/Web._Blank/.kubernetes/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: %SERVICE_NAME% + namespace: %KUBERNETES_NAMESPACE% +spec: + ports: + - name: http + port: 8080 + selector: + app: %SERVICE_NAME% + type: ClusterIP diff --git a/Web._Blank/.tests/Tests.Web.Blank/Properties/DoNotParallelize.cs b/Web._Blank/.tests/Tests.Web.Blank/Properties/DoNotParallelize.cs new file mode 100644 index 00000000..6a076669 --- /dev/null +++ b/Web._Blank/.tests/Tests.Web.Blank/Properties/DoNotParallelize.cs @@ -0,0 +1,3 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +[assembly: DoNotParallelize] \ No newline at end of file diff --git a/Web._Blank/.tests/Tests.Web.Blank/Tests.Web.Blank.csproj b/Web._Blank/.tests/Tests.Web.Blank/Tests.Web.Blank.csproj new file mode 100644 index 00000000..75b187ba --- /dev/null +++ b/Web._Blank/.tests/Tests.Web.Blank/Tests.Web.Blank.csproj @@ -0,0 +1,23 @@ + + + + net10.0 + false + latest + + + true + + + + + + + + + + + + + + diff --git a/Web._Blank/Dockerfile b/Web._Blank/Dockerfile new file mode 100644 index 00000000..119abdb6 --- /dev/null +++ b/Web._Blank/Dockerfile @@ -0,0 +1,35 @@ +ARG DOTNET_SDK_VERSION +ARG DOTNET_ASPNET_VERSION +ARG CONTAINER_REGISTRY_SOURCE_LABEL +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +LABEL org.opencontainers.image.source=CONTAINER_REGISTRY_SOURCE_LABEL + +EXPOSE 8080 +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:$DOTNET_SDK_VERSION AS build +ARG NUGET_HOST +ARG NUGET_USERNAME +ARG NUGET_PASSWORD + +WORKDIR /src +COPY . . + +RUN dotnet nuget add source $NUGET_HOST -n private -u $NUGET_USERNAME -p $NUGET_PASSWORD --store-password-in-clear-text +RUN dotnet build -c Release -o /app + +FROM build AS publish +RUN dotnet publish -c Release -o /app + +FROM base AS final +WORKDIR /app +COPY --from=publish /app . +ENV COMPlus_EnableDiagnostics=0 +ENV DOTNET_USE_POLLING_FILE_WATCHER=true + +ENTRYPOINT ["dotnet", "Web.Blank.dll"] \ No newline at end of file diff --git a/Web._Blank/LICENSE b/Web._Blank/LICENSE new file mode 100644 index 00000000..006e8c21 --- /dev/null +++ b/Web._Blank/LICENSE @@ -0,0 +1,18 @@ +The MIT License (MIT) +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Web._Blank/README.md b/Web._Blank/README.md new file mode 100644 index 00000000..f5864695 --- /dev/null +++ b/Web._Blank/README.md @@ -0,0 +1,28 @@ +# Web.Blank + +> _Minimal (blank) Nano Web application._ +_All lessons are complete, self-contained examples that include build and deployment setup._ + +> ⚠️ _To run this solution, the [Nano.Library](https://github.com/Nano-Core/Nano.Library) repository must be checked out in the same root directory. +Nano is referenced directly from source (not via NuGet packages) and is expected to be located in the .nano solution folder._ + +> ⚠️ Remember to set the docker-compose project as startup project, before running the solution in Visual Studio. + +*** + +## Table of Contents +* [Summary](#summary) + +## Summary +This application represents the most minimal (blank) Nano Web application setup. + +Its purpose is to demonstrate the required boilerplate, file structure, and general configuration needed to create a Nano Web application. +The application itself is intentionally minimal and does not expose HTTP endpoints or configure additional features. Instead, it serves as a baseline +from which all other Web lessons and examples are built. + +An `App.razor` has been added to show simple use of `IComponent` when registering razor pages. + +It is recommended to review this application first to understand how a Nano Web application is generally structured and to become familiar with the purpose of the core building blocks +used in the boilerplate. + +> 📖 Learn more about **[Nano Solution Composition](https://github.com/Nano-Core/Nano.Library#solution-composition)**. diff --git a/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj b/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj new file mode 100644 index 00000000..df35117b --- /dev/null +++ b/Web._Blank/Web.Blank.Models/Web.Blank.Models.csproj @@ -0,0 +1,75 @@ + + + + net10.0 + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + True + true + true + true + Michael Vivet + Michael Vivet + $(ProjectName) + Example project demonstrating a focused aspect of the Nano Library. + + A practical example highlighting a specific area or scenario of Nano for learning + and experimentation. Shows how features or patterns can be applied without + representing a full application or complete reference. + + + - Added .NET 10 support + + git + master + https://github.com/Nano-Core/Nano.Examples.git + $(GitCommitHash) + true + true + true + snupkg + true + $(Version) + nano;example;sample;framework;library;learning;showcase + https://github.com/Nano-Core/Nano.Library/$(ProjectName) + LICENSE + icon.png + README.md + true + + + + True + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Web._Blank/Web.Blank.sln b/Web._Blank/Web.Blank.sln new file mode 100644 index 00000000..f81e7296 --- /dev/null +++ b/Web._Blank/Web.Blank.sln @@ -0,0 +1,140 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.1.11312.151 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{F9091FFF-8BDF-4447-A4CC-19ACF2F783A3}" + ProjectSection(SolutionItems) = preProject + .dockerignore = .dockerignore + .gitignore = .gitignore + Dockerfile = Dockerfile + icon.png = icon.png + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docker", ".docker", "{06253892-53F5-4EB1-8CBE-8558DBDD9B70}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".tests", ".tests", "{7E0D2A84-F3DE-4406-BE71-30EE6723E6D0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".kubernetes", ".kubernetes", "{0C4D782F-72EF-42B4-BC1B-AF4044CC863A}" + ProjectSection(SolutionItems) = preProject + .kubernetes\autoscaler.yaml = .kubernetes\autoscaler.yaml + .kubernetes\configmap.yaml = .kubernetes\configmap.yaml + .kubernetes\deployment.yaml = .kubernetes\deployment.yaml + .kubernetes\service.yaml = .kubernetes\service.yaml + EndProjectSection +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", ".docker\docker-compose.dcproj", "{557A0C48-DA6A-4D7C-8668-94F08A390F4B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nano", ".nano", "{3339F5B7-AA56-4192-B201-75B2620036B1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nano.App", "..\..\Nano.Library\Nano.App\Nano.App.csproj", "{50CBB29E-F271-4BD3-B176-6F68BB3294EE}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{426FE279-89DF-4C2A-ABBB-729FDA4D893A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{5C605BF3-D5A3-4334-8AFA-4D45A6DC365E}" + ProjectSection(SolutionItems) = preProject + .github\config\slack.yml = .github\config\slack.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{F547FB13-43D4-4AAB-8629-B41FFF4A251F}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build-and-deploy.yml = .github\workflows\build-and-deploy.yml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Common", "..\..\Nano.Library\Nano.Common\Nano.Common.csproj", "{F1E2F880-3214-E0ED-03BF-4F78931C9C6E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Data.Abstractions", "..\..\Nano.Library\Nano.Data.Abstractions\Nano.Data.Abstractions.csproj", "{023BB767-08ED-44DE-A6BF-9A2EDE8345B7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Eventing.Abstractions", "..\..\Nano.Library\Nano.Eventing.Abstractions\Nano.Eventing.Abstractions.csproj", "{7F25283D-B5C5-8CA9-8722-E84B380905C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Logging.Abstractions", "..\..\Nano.Library\Nano.Logging.Abstractions\Nano.Logging.Abstractions.csproj", "{6BE9DA86-B487-64ED-7BF6-08CA10A05A97}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.Storage.Abstractions", "..\..\Nano.Library\Nano.Storage.Abstractions\Nano.Storage.Abstractions.csproj", "{F0FA24A3-0F7B-3962-C471-78992174D4B6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Api", "..\..\Nano.Library\Nano.App.Api\Nano.App.Api.csproj", "{E55DCB7C-9082-3538-6288-5A6E5C8DE183}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Blank.Models", "Web.Blank.Models\Web.Blank.Models.csproj", "{55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web.Blank", "Web.Blank\Web.Blank.csproj", "{77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Web.Blank", ".tests\Tests.Web.Blank\Tests.Web.Blank.csproj", "{B008D867-12B4-7EB7-D707-1C1503E4151A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nano.App.Web", "..\..\Nano.Library\Nano.App.Web\Nano.App.Web.csproj", "{AA77035F-A3A8-7972-CC88-17A2E41EBA87}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {557A0C48-DA6A-4D7C-8668-94F08A390F4B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50CBB29E-F271-4BD3-B176-6F68BB3294EE}.Release|Any CPU.Build.0 = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E}.Release|Any CPU.Build.0 = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7}.Release|Any CPU.Build.0 = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7F25283D-B5C5-8CA9-8722-E84B380905C6}.Release|Any CPU.Build.0 = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97}.Release|Any CPU.Build.0 = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F0FA24A3-0F7B-3962-C471-78992174D4B6}.Release|Any CPU.Build.0 = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E55DCB7C-9082-3538-6288-5A6E5C8DE183}.Release|Any CPU.Build.0 = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55CA3ADE-88B1-B763-9FAB-EE5D4F418EB8}.Release|Any CPU.Build.0 = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {77596F3E-C6F4-A01F-B4D9-9370C3ED3CBC}.Release|Any CPU.Build.0 = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B008D867-12B4-7EB7-D707-1C1503E4151A}.Release|Any CPU.Build.0 = Release|Any CPU + {AA77035F-A3A8-7972-CC88-17A2E41EBA87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA77035F-A3A8-7972-CC88-17A2E41EBA87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA77035F-A3A8-7972-CC88-17A2E41EBA87}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA77035F-A3A8-7972-CC88-17A2E41EBA87}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {557A0C48-DA6A-4D7C-8668-94F08A390F4B} = {06253892-53F5-4EB1-8CBE-8558DBDD9B70} + {50CBB29E-F271-4BD3-B176-6F68BB3294EE} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {5C605BF3-D5A3-4334-8AFA-4D45A6DC365E} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F547FB13-43D4-4AAB-8629-B41FFF4A251F} = {426FE279-89DF-4C2A-ABBB-729FDA4D893A} + {F1E2F880-3214-E0ED-03BF-4F78931C9C6E} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {023BB767-08ED-44DE-A6BF-9A2EDE8345B7} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {7F25283D-B5C5-8CA9-8722-E84B380905C6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {6BE9DA86-B487-64ED-7BF6-08CA10A05A97} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {F0FA24A3-0F7B-3962-C471-78992174D4B6} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {E55DCB7C-9082-3538-6288-5A6E5C8DE183} = {3339F5B7-AA56-4192-B201-75B2620036B1} + {B008D867-12B4-7EB7-D707-1C1503E4151A} = {7E0D2A84-F3DE-4406-BE71-30EE6723E6D0} + {AA77035F-A3A8-7972-CC88-17A2E41EBA87} = {3339F5B7-AA56-4192-B201-75B2620036B1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72ED70A7-E1BB-4D06-B6FD-E092DA575FBE} + EndGlobalSection +EndGlobal diff --git a/Web._Blank/Web.Blank/App.razor b/Web._Blank/Web.Blank/App.razor new file mode 100644 index 00000000..bc9fa62f --- /dev/null +++ b/Web._Blank/Web.Blank/App.razor @@ -0,0 +1,11 @@ +@* App.razor *@ +

Hello from App root component

+ + + + + + +

Sorry, there's nothing at this address.

+
+
\ No newline at end of file diff --git a/Web._Blank/Web.Blank/Dockerfile.Local b/Web._Blank/Web.Blank/Dockerfile.Local new file mode 100644 index 00000000..207af714 --- /dev/null +++ b/Web._Blank/Web.Blank/Dockerfile.Local @@ -0,0 +1,6 @@ +ARG DOTNET_ASPNET_VERSION="10.0" +FROM mcr.microsoft.com/dotnet/aspnet:$DOTNET_ASPNET_VERSION AS base + +EXPOSE 8080 + +ENTRYPOINT ["dotnet", "Web.Blank.dll"] \ No newline at end of file diff --git a/Web._Blank/Web.Blank/Program.cs b/Web._Blank/Web.Blank/Program.cs new file mode 100644 index 00000000..73873dff --- /dev/null +++ b/Web._Blank/Web.Blank/Program.cs @@ -0,0 +1,11 @@ +using Nano.App.Web; +using Web.Blank; + +NanoWebApplication + .ConfigureApp() + .ConfigureServices(_ => + { + // Blank + }) + .Build() + .Run(); \ No newline at end of file diff --git a/Web._Blank/Web.Blank/Properties/InternalsVisibleTo.cs b/Web._Blank/Web.Blank/Properties/InternalsVisibleTo.cs new file mode 100644 index 00000000..b7695e63 --- /dev/null +++ b/Web._Blank/Web.Blank/Properties/InternalsVisibleTo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tests.Web.Blank")] \ No newline at end of file diff --git a/Web._Blank/Web.Blank/Web.Blank.csproj b/Web._Blank/Web.Blank/Web.Blank.csproj new file mode 100644 index 00000000..b6e99c72 --- /dev/null +++ b/Web._Blank/Web.Blank/Web.Blank.csproj @@ -0,0 +1,36 @@ + + + + net10.0 + 10.0.0.0 + 10.0.0.0 + 10.0.0.0 + AnyCPU + Debug;Release + latest + en-US + enable + LICENSE + true + + false + true + false + true + Linux + ..\docker-compose.dcproj + + + + True + + + + + + + + + + + \ No newline at end of file diff --git a/Web._Blank/Web.Blank/_Imports.razor b/Web._Blank/Web.Blank/_Imports.razor new file mode 100644 index 00000000..665e8283 --- /dev/null +++ b/Web._Blank/Web.Blank/_Imports.razor @@ -0,0 +1,2 @@ +@using Microsoft.AspNetCore.Components +@using Microsoft.AspNetCore.Components.Routing \ No newline at end of file diff --git a/Web._Blank/Web.Blank/appsettings.Development.json b/Web._Blank/Web.Blank/appsettings.Development.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Web._Blank/Web.Blank/appsettings.Development.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Web._Blank/Web.Blank/appsettings.Production.json b/Web._Blank/Web.Blank/appsettings.Production.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Web._Blank/Web.Blank/appsettings.Production.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Web._Blank/Web.Blank/appsettings.Staging.json b/Web._Blank/Web.Blank/appsettings.Staging.json new file mode 100644 index 00000000..8593c62d --- /dev/null +++ b/Web._Blank/Web.Blank/appsettings.Staging.json @@ -0,0 +1,2 @@ +{ +} \ No newline at end of file diff --git a/Web._Blank/Web.Blank/appsettings.json b/Web._Blank/Web.Blank/appsettings.json new file mode 100644 index 00000000..e8d9ff25 --- /dev/null +++ b/Web._Blank/Web.Blank/appsettings.json @@ -0,0 +1,13 @@ +{ + "App": { + "Version": "1.0.0.0", + "Hosting": { + "Root": "api", + "Http": { + "Ports": [ + 8080 + ] + } + } + } +} \ No newline at end of file diff --git a/Web._Blank/icon.png b/Web._Blank/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67567667de4c5a4ca797c66e71aa87aafc6df1e5 GIT binary patch literal 14103 zcmcJ0c{r4B)bKMS8H7QWF!rVFDrD?S_UvU}Q$ksikYz?uWFJ||5+No;N|r2>WQmA| z$i8G5vhVA6Prvv5>-+n?uDW=h`?=43&biNij%X7j9Xc8g8VG{u^mMh%AP5eAg+qvw z;KxQFaS!~U_R+QWgP_GronSV!u((SH>H?D3Ele#qE&gQ%okFUPU!P4^6=PWwg%z71hGhQ6^_j z1<5BpCy1ZF2{&&;3;Qg6WM(c*6{N)yJw@Uj+0d$ntoH?5N)fl|oN04Z%Lk0jtwu}B`OBfZqPsW{oC>0r+Ko&hN}9BVnG+hG=Z6MFwT^clMf-1kI80L7rb;fB}+ z6}Be(<{h_+#qG?h@0Sqox*Z=0N<5-evl))Y3?(JnMnn>O}WzKAt?zCiu54G{_b%VDIl{YYqIASm> zlrj`O^-IVip6{-BdB3eH)7NtlZR4)E58vTQ2W|iN3Na$j@>85)CJI?fVTch_q2>X< z^sUbnk&=+I1Mg$MxyJ7d_{Slu`8Xmc0MgtjWbfKpX#G!w4_y$~_n_s>x94h`WzUO3GMPc4H5yIV`0so5&$_TV-) zpXD7?6pB&}qiTCAP`>zc?{_h@QRjCdYr^S`ZR6vAO#C|^v9GET+xWN~UB1Q~>d6LS z3+GGzUh&^a*6lTO#Z&0|%Re{>Zw_QGe?U4*O_%rSE~jJgfinJ)uBb3A_IJnJetfu( z?2qSYeOQTc>hbfRJVVO~k>dIhd9lLdvP(NaDoT_?grby|XG>~XUGc5o5>%P0B0zHC^Vp2!%ZBb&?1`Jbkm ziF|?u6Es+xj(6K(FOXrQD-wryOCV^1&P65jeO1s&y@E-w+NxJoW`r2Kg39}TN-$!% zfSwP(3y4Pf?Z2d{6Y!7SVm^xdnLQxpPoww!2F%2hJ#_Xr!4!Y}Ln*QtFQDroNsG17 z99_QjEi`ycdnl4L#Y3iL-3qb@aSXeKY|b-py%j8LbOojU-^=xxFls9i|JaXg12I2& zf*nchxEEjGIg_*#QA9@lFO8n~i{g;z$ElWM+VU$TcA+(Q?M7*7e}rC~8_eY0*FYZ< zLg+#SpPx!9IwHU2JycZvhGAP^1y7XGFT6RA`$hn;UYvJ3QSL=zFJK->OXl8uW`O^0 z4O!U9j0yNh;7G1&tzLLdT@P_u>{Br(n68@^3Bqx7z;j$U6v7Zm+pptI9eUP;Diy%G zyVC*EL=TnMJZi8xQEC1pk$8y}VzC&I@qdWxXxsOGNk#$)b^2PhKM7I5=66h&FLx7W zB14%OV%G8S_@o&pKoRQ#ebmi*wed>G(8CSv?=Jc@20LYh(!8C5#{SzMD2nnnMyW$P z%e<8`;M~I0;4{Bn*tKN$6g51|k9>*)D{jHgK8_tspwkEJy$;x`6m%1I_G#bB82A9d zG-g$X%B7n)@XWd%rvL;IhDsvi#_J{eV~Kn@aB-`yyRqaK@o%lk*GB%NF!j43N?m!c z+bB!zKaeToG-}SBA0gd4-vAriJz23X=^Fk!bwHG4A5*G*{i(dXOJqDjcn{RjgA0I| z-T!0^a{$7#!QWjF&x`j3clV~%jOuJXQXKk6dl9!WN^NzSj23yrxwTtlKCQ2kk@)xG zt{b>f`HXK7PyUy^KkIxL#wo*b(c9*zo-B0ae;k$^!_(SP>WUoj`NQ?%07br$E3-gk z#5%&#&=2!lfWB3)1saeLe@=^hF5fpVOe9(XzWf^4R)!{$fOv>RaN-{;`Jei?`Ca6J z8(!d<(Rd*l5hZ|`#LZujSDak;Pm8KLiT{;1`wu_Y38nb!&$MGzA0y3M6N!NUrsH%I zIM{{0@;3`vkm$i~?@pse%A+K-_7JUId(DuMAxhKC&k7>KH6nIOiRXUX#U*Bug=Gc^ zi)@MP0vq8c(4n#&zc;o@&jE@5-dpJ0(>!=UV!uP8TkHC12Bgi76{?Jn84aLYNh8Yq zHa?)AJrykZKdMMeog}V$a!x-hKd!Es)2|_m3?4{ZWyta&19SC7pks_pJs|=6*7)nX z9x6ajjZ61WjRQ@wahNX5toh?jHb&rDOWhW`ezt!p#0KH>K9r};Wh`Z29UE@QGy8zj8B)*THe@%@f zC;`r>-xLB2((i`n^vm6!ohMc#P-M3@xL}-Y?z@tZxTl}NLcl6LGa}@4ppgGF_@|8c zC5+o*s-HPklEobI2)3$6zc6o)P!wJE7I1=%*dKtIi&1l&Ia9C4dF#q-eY zCCZ1gtZO*U+xvn5*+ZN*qe|qsNn}2pPhY(=J|NWi^9w&_<3UJtD~-HL8lgS2{`+O! z-n-!BBctkLRF@v-nd|K8p_<_V*a2qTS`rQHEG!R6+RLob|ZB znFm6fsbY|5f=6e%@9}K(gd(yZ`t3~V#BaUB@8-Qe^p)A5cMT}DN*+%LRd@_^ABh{=33ZyzSu5zi7fe zh#Z2o4Ar?Ejhvj)31?uQvs#IGd>R{_DIao+$IPIY#u~~`F-0|Uyx;ex-Su%9W~~T- zWk8;cR-Ek8$I+ppP}1|mlMfag^x|A$i6r-pEDGn!%J~u{J>xT0DJkLl15e*qg}!oD z5K76mgg+zA2Y+h#ygj8r*M+CtybZ;k$Qq4>g9N7}sV9bLW z$Tw0DbjJf5Brx5>0_jEGge7w1d5b~Sa9;d7^6Q8a@Wcw~(UDj9I`(ZKxmO&AyU&Qy zM-P)YoYnqZ=3V|GwsC#y`ob@4q$Pzm+B0oh-`UBf!F4lshxY)r7gHD_*Hm0iLSzW^3kL=j>_A4D&8 zjzBmPnJ0EU-6vKq0V18ip0@y(wmMd9e(igJJL_Jcf)n|8EWr9hU3V4beQ-%5;F7dE zL_?KXet?VdcLUqX%F-xC(>*rauA15J8eI1H08?U^4h@V9>)fAxu=!L#j|{sE9m2hG z_oait11@a-UInE4y{BplQesv!%K{K*=B9k1&w#qOfVvCK4Wq0B_5i~UfZ=5xDJrfJ zN?v>ec`2eKp%tp8RWl0(yKWY3ZhBzJPxj=exuvCruh{B7fEp%_7DSmKMRrfnn2 z*PHXQd*A(70P=uaGxI~DYE?i0-tYkai{~a3!f$5NoW+L$@ar3!mmolLUmPGgm3=Qz zhPV4fqJ=8B-XJ?zq6Itn#k!&nunD9WZ#c>OE5kx1pat{LYDfDDT~TH7tKvJ#)ak#T zShl7AtZ}ZN)D@K{zwJw;klLD;tr#Y=z6lU!$rC`KMGKe>V156C$Kq%J17t17j3`e5 zd3eJBHW}sut^OJSp6`Yeld21V|z2Q#=ut-y6MZ za=x}rw5*54@%j(3GE-E~$U?%H>NSpZqyIl4G|LUVEQ3dY$S(27{A6OQx@+#(!Ds!G zSqdpj9F;&bOs+lp$1pn=nquU&5&x`x6RpanInzooNM zYvyNIF@BQL&)J13%9sMP2^X4o#Bp zH*Dvl8wM$zdR8+Dfp@*~^VS?Ks&-l@-32RE!JTl5jHo`Vy9j3{18QR*KMKKZy6G%dCeh=bUHS!Kk^72=BSDU?a!s659UMt+vRLoqSB; zGolJ`0+U8H!Km83P{t;o0>Jc{6(AoDfg(<{6Agl>J7*6fB z1OcBD;p#CeR<-aS>)J-++IPGDlt?VwC{^b7-+(2?W1P%IGvOih8dN`aq8t2a2oL2c z0@MyT`bxbnn{|MAqb{DaQ6?q+0%Fy~>8N?EB)WlxR;^o+Zy2h)P~^4Ai?aca=g)<^ z2Og*1W}e8@avFcdU;{mn<&GkB=iFuL`!39f{{ws=8!xk7oI&0H3gc$suxw%n9UL2Q%DjRyX;{@+g|1hl<3JrjXAI&7Rz1(N&V;s@d z|HcABEcQ!BqoOhnEw*WES3iel-%cJg4Rwhls^?eufnW~5=cD@idrd04ssbh_$9Cf_ zB<^{jvKF*&GO{NtC`8T(yNg8jF8FtC0@NyVQ0;poErU`Z*F)k*=bt-(^f^-PnI;`G9n+E9`Z`BCU14|g$fvKeLzR!e94TRC zPmZY}*1c!UKZI^`Q~an}ZJV-~q*Cx7=qMU~`}9$|vL|huoJhGs#P{gczY*2wh(795 zypQ{+JNc!P+YB|*^-oK*<${T!qp*%tL;{WTnV;s%t$yR~8r3OnFWYO6n;9Po6D0yT z%b}v3cuovu^Ou=M^B>VeaFeptA^cZmyw^;st>XUY4-)N)TO!TJf)U%2n+l;)kU=~Z zx6tDZZ1HYLBh>?*YGsg1-foRo%|dqDNt^tG(7L)}U^KLIAp&W-zq49ge8HPAfmETo zecT@?AiOskcTCZA_1-=Ej@;t<4;vztz??r_s1Bxo=)+==A@Nks*A^lva(|ImxSnio zvhd0EH>>NQ43eHIwGOf%Yvnch6$6h|CG9Ot;JmSyF~~rE`fk@HuzLyo!1Pejpvn7H zvsbN+MmB7SFB(*Dn6JgZrw%#UMkC!*c1!8W3a98Q7yUxOSg&ePJvnvETf%VW%5e`= z`!Ii?%y~ZbzD?|tPm4B2r*vv^ej#yH7NPS+gRKqY{!b_C=&?_4 z950Bvjx<7YMySi}bo|AK@_laoAR@27H^1?vy@?rVg#Bu{Y@>GxYqPDo+g{gWaeZji z4HTDL&eYDI(W(omdvIG_XUCL;vD?^!(;FTJFG)cgWtGrRz~x8{31YK<)6s?k z08y^i;e8l?;p_O^rd>fd#@OW6nx|u+R{T6@WF?Yo+wtol6%TN;_`T5z0S z9s5ys_tHx`^VFmKeoz#7n(Nm;K$+b5Awu1C(6~nOni&K3AkIQPH%c=|xA2HX_bG@MPS1#;# zr2&aI^p7M|APbD)M)$F{)B0tevvW-KpMWv$e%AJnr^-D6x%4aym{r7Gi z+e5S&SH>awg`JUJ)tntWn2F8d`UU6Jc1N4neQh-t{iDok1NS5o$BdjF5lv}aWXCbp zul5N<^=m1VYV%wd!Z-;+_N68D3fsHKX=p9H-TA+v$YX|;7pG`r?jf5}>;@0t5lyc} zf{!DXXt7;H8RA11rI#|sA66o7+O!SeGZ<@N?3rkNb$^ZO?BYo;{G$?3I_bT1e>e=e zbp@p<;mLjhZLjR{A*QfQb6nV|9$?|c8+6ooaLKE2vXJ!&rP_DPxHB>#V*aNpYkfrf z*!qpKUfr>0Rm|P8by^^r+B73Ycgt${KX!UC(PCdLR|c!l)sa80JC6J!`SCq3CPk`6 zRnh)WM}fZA$PG;MB8Kwgb3e*Nb%lu((Dz(xF&s@~FKb$8E~#w`s5#GibW(#Ak~KZ0 zb#SBf!9M?NL$whJc}L)$orcp0$e8Ug8g+M%d{2J`g@<*n(Cr2_TRTnL$nyvfM_tq_ ziP)@A9?oJG`?hvogZP{4x)8*Gl?4sdFl{BKu1QQvO@#^EYx2f3&-qJ+T7W5FP85ahBQ*=HsdF z61JtfaT2`bwzCR~wY=chmr`&k3~~!9B~LSZaJ`9uOKpIi~Zz9jeM0^$N&V z+|Nc&?*ndgj{SUklR)cVt&pYu3yHg+PN;nFr=%tNvtyI~*X4Sa=tV%U9?Q)>!1R`) zmQ$dlc`BuUD`X*v!OJ$a!$l-q*}U~EKH#qZFt0xXNR6ojPou=j7E#8+CVXjSg2+eX z*+m?|%9??%)Leln0>MV!^sX0PplBNY>2}8?AM7$#(6is4c4o{q&7PbsLV;3Q&G$3R`-SwNj(>Q)bej3j z4f~ZQHH}8PqR8Bc-zS@;pRxDz*VR;VPac$Rv-gV#B!02A{R{QHeEE@0Hw^oRQf-rM z{rm9OqK8&7-YT*YWnL=pw6Gt*!2z7x97yO~z3~cKGUdp&ck9@?(G2 zFeQP6rvM(rQJ^(IV_Okax>)BL&#nA8&+}GgI3c{a%wW+jCf!>tN|SoxyPmT6hnHD# zh8+*+}dEu#u^Pimdn-#J5*7 zEi;~J*$iwDVdBu6GQw~2=Lj*YlyAe7If6Iy>49*CMwZ_sA$na>fiJkeR$!Haqg4AV ze299R|5f}r`26FVE8qAeZtMR$&>Bdb(=`))O}R2X^?XWxND^H(%u=(_asuedEO`9)-dtxzAN%HgNrz`vv6aU58c+P;tYcZ@ z9U?Z3&+vq>M!Hs~h+5#8CWDBB8yeNG5dnE(?}ASKc~0abQ-GXRI4-uDTw_?HJ#{;J zd62-xFU1@b9h=r36fIr{uhZpr2w!13tQB4*4LRg;to>=|Pd^ucrR2@kIrHwJ%kL9p zt~FD{A7h_~ zf6}kC{T&-A$Mz(}!*V_MrvkITPSC3TyzHfm%6;;Se6N=j$oDE%JJ_A1n6i$BJPqq3 z;dTiPuKXywWV9B@{Yk|aG7`}bKSV{1B^&td-1I|C1OUsaKYd??GaEFXfa%<<+#+?u z{LxqS-aM+q9Xu5K$|7Zum1Ch%E$SZ;3Bp{*^@Nc7F$P~{%C~Nl5RtMTrH#dkoV*QDK{iYVWhEWk5A)2xdXung4qFb5V7(eCYcuKwIEoh40K zZ>a3}=TC96X-kU-L_Su~(GfV1@Q0|e+ zlrqpQy}$-w(v~+kRy2Sksn`7gZP*<1sltvxw2%=LZL<~kpL^A=5%7mnMg>CdXfk*$ zlnQ9FSzm$|6~y<-X|R81%q_RH{QwooXp}=|OK(xzyX!F2q_MZWj_(mZBJky!%pOXC zvU5wD-?IGM4-dEN9EJSGF43)!qC?lwd|XNB=BL1%u$R@XAf<(X*Rjez6!MQq-ALJV zONumx`GMY{DdXe>7gM$|%JnjEz_AMqMKFvmN>hmCi>kt%IX@6Tl;k}brsaBx);4$= zlJEr`24YNa099AMfIbKJZ7suG=T=Z&{{f8eNZdcRpQ%;mtfCMIe?USTUhC~x4q9ap zYdU!r0OmqEEhiSY*D_s~DtFK(CN zH9tfOJn0gq?E2<7*Zhs$oNw5xwZu6b9BH!=ccb+Pjixz12XksvcmbgRJ5ToK%0xNQOW;A<)@u5L~8xnIX+AE1LTjpI)t)(OWvjL zv*?7WwjU{ILy&tBxvmFrKcS*{<174gaICn$z-J>dcs_mCi=XXBb`{eRi#=+2Vx{Y& zVl@$-a}%xV4^`sZws*hnal7HZkQI-;H@yjfFd$vtl#y?#fTt<*X z*iz%Fl1twhm&W#2ye49&H!(OcXobG{5{?jqpDKCvT&v6JX6vV2yIBeRmy2e)C^aF! z#p=9h=Um5r=Ok5q)rRKl@!Kv3=Z!{oVv4LJqbSZuPq8CJC>D^ZG9#zmLPYr-3=760 zxj2NS4j=uF0dcM@Df(z-kC~vo4pLD@Tgc`{h-6B9=?>{f(^zo*hlUI8$VXJD6BF#? z_6ADX4N#p1;`$t^I$hw)hXU0akWCXqTH`tOPNjXv6y`9$qs)g8T2ux|NxI2mG*36b#k7UQdVFt11yu01Zj9K17*%hC0Zqo@oN=}#!6$m zynTuClLoi0vO+>k6nRTEEKOdZYIKiQt*2Gwhpdn18}FCVzq`{1UWQ+IlH~S<_qLJK z*`&`%O99$Pd?obGCuO>Lo1Y_Zs%64c+@h3PGQ*7T-d;)l0s7J==)9#+speI+Yt_^v zaFfMV0$<3TBKYe?e}=!F;|%Uo#V3eE`kr1}HwcfaiMH!S ztCSj1plVYJEYk>_G)GSNWO4U+M>-&n{YrIqy0e1iE#g z;+o?UmE{Nt10850JzSKIAL$kNRDcG(GxnCH6;Yo{;|=x_=bFtG?&Jl7!0LB2D9y;s z9Lh6K34s%w_%Sy+u1Eso_<1Eq& z=y3kG^63c6fjU&79e$2}%CV$Su@qY5B_|6?V5dL_yJ6W&H3sIa>J&mc>yb`YVRzfQ zM0At|D>>OeuwS;D@N202c@f>zfR%sfu9_S@$epRO_zqBZ{vGfT*Ow1jE58M5kL(|@ zsB9l(IXqfQfH2-6(z5Mh%{z?CDF%o?;7mWQvSq@D(PDrJm;eJ$l(=uq2$F1$;#h!nU}&wSi%;!t1>i3>*cki zn10q~^G7gL|B)iMXV*~xHlf`h$d$8pZEU?@sNZJ7Gj=2hT`9ZGfA`MFI=*K~xa{w@ z`3#k0K-Wz^;QTse0$$e7rDCk686f@uQ_c@;Y4k5^CfQ=cr&=+niZZEHbjO23?O3VR z^eF?LghpYWKxAL;`=n>j#2}CC>n&MT0~N=;l=pM5ZY~Pd(Wn&9g1tQyrN&B=A@s z`Qc?I1zhX8+Z{!@L1w&=?mG)F1dXiwH;fLLLFyV-maTmMiRGD!FAhj!Snb|QP@iQI z50a{(oAyBW>$D^!+toAG#lJF>ig8RE{_>tUkQNj0;KQ$%n2ke4*e}SohBgO3Ri=Iv z)X-2M?~9}9WPxN`dIGZEJFS&HsXmtysMtEzXi%m)9jq8zXf_C&ysTClX8nQd__EFo zMG*|1T*Nt)sQ5FMCuuuu9yOR9JGk1A(si%t(Q@R04uRgkQ&at8b&RwMb<^4HO@q=e zEKuJs(}0(Bo5O3E4$s8Uz!YH&mUeURyx%a}iN!mMI~{$WH~4!WhbL_Ia~3 zrlYZe(us!X+kvq*s`hr%xG%cmOG~)hLcx%{PS-U&X^G^=*}R}s3tjI|#U#|SVB4Dy z1n36W98;NjhU3^945ME1iid?5M$@Xbpr$86D!yYhWv6vLXj!4s+Po#DhjT{6CR!Y!fuZ{Kuggs*Kf=HER zjk5{ut|8B8$BvysANKc9Gc=@$bLl!^oRTRSPT!nd>ez90+3~Je@#`8skXjl``vxW% zQv5cu#(3{)qqaA<4@`&KF^n=Re;St!TQE#hVaSg`3%QeqbMvOD_slD+-q+mU$gRt1TtUW=4`>@d1H#{xQ!)eO%*|2QNY`zZe+}=auH(zFD7d>%AR&mItA#bUO z>C3N$ka8Y^YulFV;y!ngL02jZg@{T6eM&XGHP^w+fK7~20aKxQheyN#orcQuu(B)n zU)JY0R6w@|9|&WLwtJ4uqZix19-nVeQ5Vt^>j3Tb`S{vA{pkg_%igeRgK%Sg&xn&{W?6A zwyaP(U(@Kbe=Z`>i^3s@#(E&eAYk+N5svt(4z)5#p4Z5!4B+kF#;&Ntmruw|l#U2F zpTT%`pc0TTBc!?6ji>45YBn9-^wp_5etp>9s8Y3R9~dHP-5|oY{ExCuIONm(-SdK! zA~yCgDg?=1@s#=STWq6ti$rsvfZxo4)UQSP(3EwlCEBWN4aDqL$ez$z$zCPyF;xg-Z!uP=)pq`aMTsw`Prt|ESAe_8I_-4GZDFx96pRC4yBdJcTW!;pTVwMk8;lpr5?QiCg%KDSdxlSGCDN_O|M(J7!{KthOQR!6qlXlIrie-V@ zgrur0!`PUS-YUtU*G%NAmHT-_=`>AOysAC>D{A5doFZ*6(}fkwU55N}#t^fsV~0_@ zgMYb23-(!std$vXlP}Z!uRTQH@Q1lMkg@!XNn>x4CNgP;p;1nVLK}?6$gB}+_j8EA_~T) za_ni;rdkS3+qY+MBg+oI^KkiFJ=|&VqfVZFfsFMrt@~iQD)P#sa<;U^rPM~j6tx95 z>?7~N*pN{Pi#qmepL2N-`8OfNJbqc$m7NzuV^wv_2=VSzk zbbC{cCZY9d$!d!8_zqurp7Bwj8$){vG;1Oi0Ljsn4zL|!Tt@O&TdXX|d!5Oed$;r| znP-13s-xTXzzo`3>A2WXveFIP6he!^*D)TuaagK@(h7NzCCa=@iEIG`&uatj{_S(o=ql;me^o4q%VqDTwcx6u#is zKL#cZ1zB`fEg_d3KJL{q%M6)Ym45%dd`C+t!~{r}>wvO3-`!J=wV~TY89}|TCc|)_ zfwEtMD_NktpsUT@*nRZtDu~Pk3oM7>(q&_Vga;r=e?C!C|CtelpSWJz{FAKzLcyp`dN;MD;gJ26B@rb4x{==V%*7(RH!k_)( zP9hl8)SEwD3@u{Te@Bd~dt6~O&f$pJT$a-Fc;Rvd%Gxc&hJzwdGpk|U9ZBVj5iUrp7Db8MDdecC#sLVKj!33`KG4gZ&B@69;sv3TPfZn>=%Oda|Bb2eXl`i zQ7ZF%4YQL;>djrC>!FDwhJe|01tYC<&E>`<1TKF@OdB?EJX1-1I}NR^@HJG4Qw{g6 zkiBt7(L>;@e(G8fC-d?EADA*U^JBT*8&^}9C&Z$Xjt&>Z*E}_a5Ip)>IYbR`wI{#_ z1yT?w;=*kU<4wVYp`PgT4N9k$=I`O4Q>E`^pdDu`K!&rPF9ik7^kW{Jsr+3|X2bX7 z2FMjCAHrq$5*ZHp1DV^|=!aOP+n~E4h!5&z!xC)p0tm$JV^Dp?y$}h7MU{|0Kq?_j zaQcFwFkq*V#Rc@ddLG&E(^x0i*@ta8L`DGH91BN|tcgEt!~X9hA(IF@5wWBFkNR^O zuyo*mpo)1g_lkQ_2vPD{K~FzpsPsgWHYj;#jFc`TB_N!I@I0*=;-{GoQf*^Q{ojdE zN?BwCUh1RNaYQAs9F+j(YI*mTdG&7K8X$}FBH0@rouxHQ0(wH=`QF>D6D9_zyy@&e z-xwVd{)3UOx~i0n*2YtE>ye?@&C?P>s$SEZc+_ByaTao4&qCp#U~1wab8P+(TTyLT z(Y*s1{|KhwtlUqk>F9|6t|9DWx0W%Lq2LPJ8$Qrx1CYR0$h8ra2=l%4QELC)y3Bz% z*!e>9_^l%t9@q0|m^_VjT-=6MfZpf}t#C?m$;g7CTeEeRLJ0c7Uku6kV;@jk78>#D z>E~Hd^4>o@H3!O(f)20ykB`C3t6+g?34A36lL4)!0a4X(0-lYSf_^M`F3yDo!e;tG z#s;1x?iaI{wfAF9_*+_9~c9TUE*grO3uG4<^Y4*F2%^9iEo{5GJa}#4G_~W zPNy9IouI5cUJEpsC|?uO?DPk9`%kS^mqlCsu3CGNn!F&hj{htXx)oPKxGs58am8zp2&aY?V&gYM< zRh(C#~#0Bgs-3cl?#Wzko6LhcneVx+DS#@YxH0l%V z?*qYuV@A;-;Bjz!uMV$44=jq$82?=hayg(cKly_s1~KzyHYKRs>pQQZha-CawS^vV z-CI9N*d-KFcck~k*h96v@wq%#B~cSBNFhD{o+IGJ-7wo;6n6hs&gxE@T+Os6DcyJ0A`9vXtU%Z|*U$Jh)NPmuc=n4lghd5s0jO*K*n zbEQH~evT-gGJE+GxD_5BS(djGYm8+QQniA5p;t%Y<>imOMzi(rOP5O}-5KNXz6~o< zuYM^$3{AKl%xQL)g!t&;uEkM0`93)e3SC=L=8fl#V1Q@5-;tpxt~KW2raaP^Hf~

Navigate to the API endpoint (HTTPS):Open HSTS-protected endpoint

Navigate to the API endpoint (HTTPS):Open HSTS-protected endpoint